解锁你对Linux内核的疑惑,让你成为一个更好的程序员!(上)

简介: 解锁你对Linux内核的疑惑,让你成为一个更好的程序员!
前言:小编热衷于收集整理资源,记录踩坑到爬坑的过程。希望能把自己所学,实际工作中使用的技术、学习方法、心得及踩过的一些坑,记录下来。也希望想做 资深内核高级工程师的你一样,通过我的分享可以少走一些弯路,可以形成一套自己的方法,并应用到实际中。

学习Linux内核过程中,每个人都有自己的学习方法,仁者见仁智者见智。以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下。


为什么写这篇文章


刚开始学内核的时候,不要执着于一个方面,不要专注于一个子系统就一头扎到实际的代码行中去,因为这样的话,牵涉的面会很广,会碰到很多困难,容易产生挫败感,一个函数体中(假设刚开始的时候正在学习某个方面的某个具体的功能函数)很可能掺杂着其他各个子系统方面设计理念(多是大量相关的数据结构或者全局变量,用于支撑该子系统的管理工作)下相应的代码实现,这个时候看到这些东西,纷繁芜杂,是没有头绪而且很不理解的,会产生很多很多的疑问,(这个时候如果对这些疑问纠缠不清,刨根问底,那么事实上就是在学习当前子系统的过程中频繁的去涉足其他子系统,这时候注意力就分散了),而事实上等了解了各个子系统后再回头看这些东西的话,就简单多了,而且思路也会比较清晰。所以,要避免 “只见树木,不见森林”,不要急于深入到底层代码中去,不要过早研究底层代码。


我在大二的时候刚开始接触内核,就犯了这个错误,一头扎到内存管理里头,去看非常底层的实现代码,虽然也是建立在内存管理的设计思想的基础上,但是相对来说,比较孤立,因为此时并没有学习其它子系统,应该说无论是视野还是思想,都比较狭隘,所以代码中牵涉到的其它子系统的实现我都直接跳过了,这一点还算聪明,当然也是迫不得已的。


我的学习方法


刚开始,我认为主要的问题在于你知道不知道,而不是理解不理解,某个子系统的实现采用了某种策略、方法,而你在学习中需要做的就是知道有这么一回事儿,然后才是理解所描述的策略或者方法。


根据自己的学习经验,刚开始学习内核的时候,我认为要做的是在自己的脑海中建立起内核的大体框架,理解各个子系统的设计理念和构建思想,这些理念和思想会从宏观上呈现给你清晰的脉络,就像一个去除了枝枝叶叶的大树的主干,一目了然;当然,肯定还会涉及到具体的实现方法、函数,但是此时接触到的函数或者方法位于内核实现的较高的层次,是主(要)函数,已经了解到这些函数,针对的是哪些设计思想,实现了什么样的功能,达成了什么样的目的,混个脸熟的说法在这儿也是成立的。至于该主函数所调用的其它的辅助性函数就等同于枝枝叶叶了,不必太早就去深究。此时,也就初步建立起了内核子系统框架和代码实现之间的关联,关联其实很简单,比如一看到某个函数名字,就想起这个函数是针对哪个子系统的,实现了什么功能。


学习Linux内核要做好打持久战的准备,各个歼灭,稳扎稳打!信仰“如果你决定用十年的时间来学习LInux内核,那么可能用不了那么长时间就会成功。如果你决定用一年的时间来学习Linux内核,那么十年后你可能仍然什么都不精通。”


一、Linux内核简介

1.1什么是linux kernel?

内核(kernel)要负责资源管理:内存,io,cpu调度.此外,还有各种设备的驱动.内核还有两大基础组件:网络协议栈,文件系统。


内核是操作系统的核心组件,是计算机物理硬件与其上运行的进程之间的主要接口。内核通过提供对 CPU、内存、磁盘 I/O 和网络的访问,使多个应用程序能够共享硬件资源。 想象一台计算机由一系列层组成,最内层是硬件,最外层是运行在计算机上的软件应用程序。


在这个类比中,内核位于硬件和应用程序之间,因为它不仅负责管理硬件资源和执行软件程序,还负责监督这些层之间的交互。 现代计算机将内存划分为内核空间 和用户空间 。用户空间是执行应用软件的地方,而内核空间专用于运行计算机所需的幕后工作,如内存分配和进程管理。由于内核空间和用户空间的这种分离,内核所做的工作通常对用户是不可见的。


操作系统设计中,为减少系统本身的开销,往往将一些与硬件紧密相关的(如中断处理程序、设备驱动程序等)、基本的、公共的、运行频率较高的模块(如时钟管理、进程调度等)以及关键性数据结构独立开来,使之常驻内存,并对他们进行保护。通常把这一部分称之为操作系统的内核,也就是Kernel。


Linux 内核组成


Linux内核主要由5部分组成,分别为:进程调度(SCHED) 、内存管理(MMU) 、虚拟文件系统(VFS) 、网络接口(NET)和进程间通信(IPC)等 5 个子系统组成。


由系统调用层(系统调用子系统)进行统一管理,应用层通过系统调用层的函数接口与内核进行交互,用户应用程序执行的地方是用户空间,用户空间之下则是内核空间,Linux 内核正是位于内核空间中。


640.jpg

1.2Linux 内核的作用是什么?


内容有以下四项作用:

  1. 内存管理:追踪记录有多少内存存储了什么以及存储在哪里
  2. 进程管理:确定哪些进程可以使用中央处理器(CPU)、何时使用以及持续多长时间
  3. 设备驱动程序:充当硬件与进程之间的调解程序/解释程序
  4. 系统调用和安全防护:从流程接受服务请求

正确实现时,内核对用户是不可见的,它在自己的小世界(称为内核空间)中工作,从中分配内存,跟踪所有内容的存储位置。用户看到的东西(比如Web浏览器和文件)叫做用户空间。这些应用程序通过系统调用接口(SCI)与内核交互。


可以这样理解:内核就像一个忙碌的私人助理,为高管(硬件)服务。助理的工作是将员工和公众(用户)的信息和请求(流程)传递给高管,记住存储的内容和位置(内存),并确定谁可以在任何给定的时间访问高管,以及会议时间有多长。


1.3学习Linux内核准备工作

  1. 熟悉C语言,这个是最基本的
  2. 了解编译连接过程,如果写过ld、lcf类的链接文件最好,这样就能理解类似percpu变量的实现方法
  3. 学过或者自学过计算机组成原理或者微机原理,知道smp、cpu、cache、ram、hdd、bus的概念,明白中断、dma、寄存器,这样才能理解所谓的上下文context、barrier是什么。

Linux内核的特点:结合了unix操作系统的一些基础概念

Linux内核的任务:

  • 1.从技术层面讲,内核是硬件与软件之间的一个中间层。作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。
  • 2.从应用程序的层面讲,应用程序与硬件没有联系,只与内核有联系,内核是应用程序知道的层次中的最底层。在实际工作中内核抽象了相关细节。
  • 3.内核是一个资源管理程序。负责将可用的共享资源(CPU时间、磁盘空间、网络连接等)分配得到各个系统进程。
  • 4.内核就像一个库,提供了一组面向系统的命令。系统调用对于应用程序来说,就像调用普通函数一样。

内核实现策略:

1.微内核。最基本的功能由中央内核(微内核)实现。所有其他的功能都委托给一些独立进程,这些进程通过明确定义的通信接口与中心内核通信。

2.宏内核。内核的所有代码,包括子系统(如内存管理、文件管理、设备驱动程序)都打包到一个文件中。内核中的每一个函数都可以访问到内核中所有其他部分。目前支持模块的动态装卸(裁剪)。Linux内核就是基于这个策略实现的。

哪些地方用到了内核机制?

  • 1.进程(在cpu的虚拟内存中分配地址空间,各个进程的地址空间完全独立;同时执行的进程数最多不超过cpu数目)之间进行通 信,需要使用特定的内核机制。
  • 2.进程间切换(同时执行的进程数最多不超过cpu数目),也需要用到内核机制。
  • 进程切换也需要像FreeRTOS任务切换一样保存状态,并将进程置于闲置状态/恢复状态。
  • 3.进程的调度。确认哪个进程运行多长的时间。

Linux进程

  • 1.采用层次结构,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程。该进程负责进一步的系统初始化操作。init进程是进程树的根,所有的进程都直接或者间接起源于该进程。
  • 2.通过pstree命令查询。实际上得系统第一个进程是systemd,而不是init(这也是疑问点)
  • 3.系统中每一个进程都有一个唯一标识符(ID),用户(或其他进程)可以使用ID来访问进程。

Linux内核源代码的目录结构

Linux内核源代码包括三个主要部分:

  1. 内核核心代码,包括第3章所描述的各个子系统和子模块,以及其它的支撑子系统,例如电源管理、Linux初始化等
  2. 其它非核心代码,例如库文件(因为Linux内核是一个自包含的内核,即内核不依赖其它的任何软件,自己就可以编译通过)、固件集合、KVM(虚拟机技术)等
  3. 编译脚本、配置文件、帮助文档、版权说明等辅助性文件使用ls命令看到的内核源代码的顶层目录结构,具体描述如下。include/ ---- 内核头文件,需要提供给外部模块(例如用户空间代码)使用。

linux内核源代码目录结构:

  1. arch: 包含和硬件体系结构相关的代码,每种平台占一个相应的目录 如 i386、ARM、PowerPC、MIPS 等。
  2. block:块设备驱动程序 I/O 调度。
  3. crypto:常用加密和散列算法(如 AES、SHA 等) ,还有一些压缩和 CRC 校验算法。
  4. Documentation:内核各部分的通用解释和注释。
  5. drivers:设备驱动程序,每个不同的驱动占用一个子目录,如 char、block、net、mtd 、i2c 等。
  6. fs:支持的各种文件系统,如 EXT、F AT、NTFS、JFFS2 等。
  7. include:头文件,与系统相关的头文件被放置在 include/linux 子目录下。
  8. init:内核初始化代码。
  9. ipc:进程间通信的代码。kernel:内核的最核心部分,包括进程调度、定时器等,而和平台相关的一部分代码放在 arch/*/kernel 目录下。
  10. lib:库文件代码。
  11. mm:内存管理代码,和平台相关的一部分代码放在 arch/*/mm 目录下。
  12. net:网络相关代码,实现了各种常见的网络协议。
  13. scripts:包含用于配置内核的脚本文件。
  14. security:主要包含 SELinux 模块。
  15. sound:ALSA、OSS 音频设备的驱动核心代码和常用设备驱动。
  16. usr:实现了用于打包和压缩的 cpio 等。

二、Linux内核组成

2.1系统调用接口

SCI 层提供了某些机制执行从用户空间到内核的函数调用。正如前面讨论的一样,这个接口依赖于体系结构,甚至在相同的处理器家族内也是如此。SCI 实际上是一个非常有用的函数调用多路复用和多路分解服务。在 ./linux/kernel 中您可以找到 SCI 的实现,并在 ./linux/arch 中找到依赖于体系结构的部分。

2.2操作系统原理

操作系统(英语:Operating System,缩写:OS)是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境,操作系统可以分为桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统等。

2.3进程管理子系统

进程管理的核心就是进程的调度。在 Linux 内核中,进程调度的单元是进程,进程调度控制系统中的多个进程对 CPU 的访问,从宏观上看,系统中的进程在 CPU 中是并发执行的。此外内核通过系统调用提供了应用程序编程接口,例如:创建新进程(fork,exec),结束进程(kill,exit),并且提供了控制进程,同步进程和进程间通信的接口。

进程管理还包括处理活动进程之间共享CPU的需求。内核实现了新的调度算法,无论多少个线程争夺CPU,都可以在固定的时间内运行。这种算法被称为O(1) scheduler,这意味着它调度多个线程所用的时间与调度一个线程所用的时间相同。O(1)调度器也可以支持多处理器(称为对称多处理器或SMP)。您可以在中找到流程管理的源代码。/linux/kernel,以及。/linux/arch。

进程调度子系统包括4个子模块(见下图),它们的功能如下:

640.jpg

1) Scheduling Policy,实现进程调度的策略,它决定哪个(或哪几个)进程将拥有CPU。

2) Architecture-specific Schedulers,体系结构相关的部分,用于将对不同CPU的控制,抽象为统一的接口。这些控制主要在suspend和resume进程时使用,牵涉到CPU的寄存器访问、汇编指令操作等。

3) Architecture-independent Scheduler,体系结构无关的部分。它会和“Scheduling Policy模块”沟通,决定接下来要执行哪个进程,然后通过“Architecture-specific Schedulers模块”resume指定的进程。

4) System Call Interface,系统调用接口。进程调度子系统通过系统调用接口,将需要提供给用户空间的接口开放出去,同时屏蔽掉不需要用户空间程序关心的细节。

(1)进程与程序的区别:

  • 程序:存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体。
  • 进程:是一个执行中的程序,它是动态的实体。

(2)进程的四要素:

  • 有一段程序供其执行,这段程序不一定是某个进程所专有,可以与其他进程共用。
  • 有进程专有的系统堆栈空间(也可以称之为内核堆栈空间)。
  • 在内核中有一个 task_struct 数据结构,即进程控制块。有了这个数据结构,进程才能被内核调度器识别并参与内核调度,除此之外它还记录着进程所占有的各项资源。
  • 除上述的专有的系统堆栈空间外,进程还需要有独立的用户堆栈空间,这就是 mm_struct 数据结构,该数据结构位于 task_struct 结构中,字段名称为 mm。

(3)进程的堆栈:

  • 内核在创建一个新的进程(创建进程控制块 task_struct) 时,为进程创建堆栈。
  • 一个进程有 2 个堆栈,即用户堆栈和系统堆栈;用户堆栈的空间指向用户地址空间,内核堆栈的空间指向内核地址空间。
  • 当进程在用户态运行时,CPU 堆栈指针寄存器指向用户堆栈地址,使用用户堆栈。
  • 当进程运行在内核态时,CPU 堆栈指针寄存器指向的是内核堆栈空间地址,使用内核堆栈。

(4)进程与线程的区分:

  • 进程:四个要素是必要条件
  • 用户线程:有共享的用户空间
  • 内核线程:没有用户空间,即 mm_struct 为 NULL

简单区分如图所示:

640.jpg

(5)进程调度:

  • 进程调度是进程管理子系统中最重要的一个功能,是一个管理进程之间使用 CPU 资源的管理程序。
  • 进程调度器有效地为各个进程分配其使用的 CPU 资源时间,同时又要达到良好的用户体验效果,此外调度器还需要解决一些互相冲突的情况,例如既要实现实时任务的最小化响应时间, 又要最大限度地提高 CPU的总体利用率等。
  • 在 Linux2.6 版本之后,进程调度器使用新的进程调度算法——Completely Fair Scheduler,简称 CFS,即完全公平调度算法。该算法会按所需分配的计算能力,向系统中每个进程提供最大的公正性,它负责将 CPU 资源,分配给正在执行的进程,目标在于最大化程式互动效能,最小化整体 CPU 的运用,这个算法使用红黑树来实现,算法效率为 O(log(n))。

(6)进程状态:

640.jpg

640.png

2.4内存管理子系统

主要作用是保证系统安全访问内存区域,且绝大部分 CPU 都是支持内存管理单元的(Memory Management Unit,MMU)

内存管理子系统负责管理每个进程完成从虚拟内存到物理内存的转换,以及系统可用内存空间。

内存管理的硬件按照分页方式管理内存,分页就是把系统的物理内存按照相同大小等分,每个内存分片称作内存页,通常内存页大小是 4KB。内存管理子系统要管理的不仅是 4KB 缓冲区,它提供了对 4KB 缓冲区的抽象,例如 slab 分配器。这种内存管理模式使用 4KB 缓冲区为基数,然后从中分配管理结构,并跟踪内存页使用情况。系统就支持动态调整内存使用情况。

Linux 还支持内存交换,因为 Linux 中使用的是虚拟内存,当物理内存不足时,内存管理子系统会将内存暂时移到磁盘中,在物理内存充裕时又将内存页从磁盘移到物理内存中。

在 32 位的系统上,每个进程都最大享有 4GB 的内存空间,因为由于 32 位的系统寻址空间只有4G,当然这是虚拟内存,0~3GB 是属于用户内存空间,3~4GB 是属于系统内存空间,实际上用户的程序几乎使用不完那么大的用户空间,一旦超出将无法正常运行,当然系统内存空间与用户内存空间是可以调整的。

内存管理主要提供对内存资源的访问控制。Linux系统会在硬件物理内存和进程所使用的内存(称作虚拟内存)之间建立一种映射关系,这种映射是以进程为单位,因而不同的进程可以使用相同的虚拟内存,而这些相同的虚拟内存,可以映射到不同的物理内存上。

内存管理子系统包括3个子模块(见下图),它们的功能如下:

640.jpg

(1)Architecture Specific Managers,体系结构相关部分。提供用于访问硬件Memory的虚拟接口。

(2)Architecture Independent Manager,体系结构无关部分。提供所有的内存管理机制,包括:以进程为单位的memory mapping;虚拟内存的Swapping。

(3)System Call Interface,系统调用接口。通过该接口,向用户空间程序应用程序提供内存的分配、释放,文件的map等功能。

相关文章
|
1天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
14 6
|
1天前
|
机器学习/深度学习 负载均衡 算法
深入探索Linux内核调度机制的优化策略###
本文旨在为读者揭开Linux操作系统中至关重要的一环——CPU调度机制的神秘面纱。通过深入浅出地解析其工作原理,并探讨一系列创新优化策略,本文不仅增强了技术爱好者的理论知识,更为系统管理员和软件开发者提供了实用的性能调优指南,旨在促进系统的高效运行与资源利用最大化。 ###
|
4天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
2天前
|
缓存 网络协议 Linux
Linux操作系统内核
Linux操作系统内核 1、进程管理: 进程调度 进程创建与销毁 进程间通信 2、内存管理: 内存分配与回收 虚拟内存管理 缓存管理 3、驱动管理: 设备驱动程序接口 硬件抽象层 中断处理 4、文件和网络管理: 文件系统管理 网络协议栈 网络安全及防火墙管理
19 4
|
4天前
|
人工智能 算法 大数据
Linux内核中的调度算法演变:从O(1)到CFS的优化之旅###
本文深入探讨了Linux操作系统内核中进程调度算法的发展历程,聚焦于O(1)调度器向完全公平调度器(CFS)的转变。不同于传统摘要对研究背景、方法、结果和结论的概述,本文创新性地采用“技术演进时间线”的形式,简明扼要地勾勒出这一转变背后的关键技术里程碑,旨在为读者提供一个清晰的历史脉络,引领其深入了解Linux调度机制的革新之路。 ###
|
6天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
30 4
|
7天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
9天前
|
缓存 Linux
揭秘Linux内核:探索CPU拓扑结构
【10月更文挑战第26天】
26 1
|
9天前
|
缓存 运维 Linux
深入探索Linux内核:CPU拓扑结构探测
【10月更文挑战第18天】在现代计算机系统中,CPU的拓扑结构对性能优化和资源管理至关重要。了解CPU的核心、线程、NUMA节点等信息,可以帮助开发者和系统管理员更好地调优应用程序和系统配置。本文将深入探讨如何在Linux内核中探测CPU拓扑结构,介绍相关工具和方法。
11 0
|
7天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
14 0
下一篇
无影云桌面