总体架构
Linux内核在C/C++程序员的技术栈中占据着举足轻重的作用,不仅仅是考验一位程序员扎实的基本功,也考验对于整个架构的熟悉程度。理解linux内核中整个的实现之后,会对自己做项目时底层的通信以及数据存储等有很大的帮助。
Linux内核总共有五大部分组成。进程管理,内存管理,设备驱动,虚拟文件系统,网络。
进程管理
进程管理主要是对于进程管理及进程调度,控制进程合理的访问CPU。下图是linux内核中,进程管理的模块划分图,主要有4个部分组成:
上图中看出用户层通过system call Interface调用到内核中,Architecture-independent Scheduler这个模块会和调度策略(“Scheduling Policy)合作来决定接下来哪个进程获取到CPU,然后Architecture-specific Schedulers 这个模块主要是用于将CPU的控制抽象为接口,这些控制主要是在suspend和resume的时候使用,牵涉到CPU的寄存器以及汇编指令的操作等。总结下来就是由Schedule Policy,System Call Interface,Architechture-Independent Scheduler以及Architecture-specific Schedulers这四个模块组成。
内存管理
内存管理的主要工作是对于机器内存的资源访问的控制,多个进程可以安全的共享内存空间。同时还提供出虚拟内存的机制,可以让进程使用多余机器物理内存的空间。虚拟内存的机制实际就是进程先将未使用的那部分资源存到非易失内存中,需要使用的时候再加载到内存中进行调用。虚拟内存的机制实际上是物理内存和进程所使用的内存形成一种映射关系。这种映射以进程为单位,因此不同的进程可以使用相同的虚拟内存,而这些相同的的虚拟内存可以映射到不同的物理内存上面。
下图是内存管理的组成部分:
同样也是有统一的系统调用接口来分割内核空间和用户空间。而对于最下层的物理内存来说,则需要 Architecture Specific Managers来提供抽象的接口进行操作。中间的 Architecture Independent Manager这个就是提供内存管理的机制,包括进程的memory mapping以及虚拟内存的映射。
虚拟文件系统
虚拟文件系统,简称VFS。主要是将不同功能的设备通过统一的文件接口来访问,这也就是一切皆为文件的由来。但linux上面并没有做到真正的一切皆为文件,比如网络,内存等访问还不是文件。
VFS的功能是管理各种各样的文件系统,屏蔽他们之间的差异,以统一的方式为用户提供文件操作的接口。
linux内核除了传统的文件磁盘系统抽象为虚拟文件系统外,设备文件系统、内存文件系统等也都是抽象出的子系统。下图是文件系统模块图:
图上可以看出:系统调用接口和设备驱动这两个分别是用来抽象用户的操作以及硬件设备的。然后就是设别独立接口,这个模块定义了描述硬件设备的统一方式所有的设备驱动都遵循这个定义,可以降低开发的维度。对应的上层也会有类似的接口来向上封装出接口,来屏蔽下层的硬件形态,用户只需要获取硬件设备和逻辑文件接口。Logic System这个表示的就是逻辑文件系统,每一个系统都会有一个对应的逻辑文件系统,实现具体的文件逻辑。
网络
网络部分主要负责网络相关的管理及通信,实现多种的网络标准。
模块图如下:
- Network Device Drivers,网络设备的驱动,和 VFS 子系统中的设备驱动是一样的。
- Device Independent Interface,和 VFS 子系统中的是一样的。
- Network Protocols,实现各种网络传输协议,例如 IP, TCP, UDP 等等。
- Protocol Independent Interface,屏蔽不同的硬件设备和网络协议,以相同的格式提供接口
(socket)。 - System Call interface,系统调用接口,向用户空间提供访问网络设备的统一的接口。
内核中用到的数据结构
在内核中其实用到了很多数据结构。有单链表,双链表,红黑树等等。
单双链表的应用
内核中,单双链表的使用有一定的局限性,数据区的数据固定不变,而实际的需求是多种多样的。因此linux内核为了能够实现一套通用的链表,将linux内核中操作方法的共通部分提取出来,把不同的部分留给需要使用的人自己处理。Linux 内核实现了一套纯链表的封装,链表节点数据结构只有指针区而没有数据区,另外还封装了各种操作函数,如创建节点函数、插入节点函数、删除节点函数、遍历节点函数等。
红黑树的应用
同样的,红黑树也在linux内核中有所使用。它为linux内核实现CFS调度打下了基础。CFS调度实现的核心思想是为维护任务提供处理器时间方面的平衡,这就意味着分配给某个任务的时间出现非平衡的状态时,需要给失去平衡的任务相应的时间。
CFS内部有一个虚拟时间来维护任务的平衡性。一个任务的优先级越高,它的虚拟时间越慢,执行的时间越长;反之一个任务的优先级越低,它的虚拟时间就越慢,执行时间就越短。CFS调度器总是让虚拟时间慢的任务先执行,从而实现每个调度实体的虚拟时间互相追赶,实现调度上的平衡。而实现调度的内部就是红黑树。红黑树插入和删除的效率都比较高,适合实现这种频繁操作的案例。
在运行过程中,核心的调度函数就是__schedule(),这个函数会取出当前红黑树中最左边的节点,然后调用pick_next_task并且导入进程的上下文环境以保证下一个进程的运行。调用结束时再将进程的上下文环境更新到context中,然后比较当前的进程和最左边的进程的虚拟时间,如果最左边的虚拟时间较小,那么就需要切换进程调度。
红黑树的另外一个应用就是虚拟内存管理。每个进程的内存空间都挂在一颗红黑树上,分配的新内存就是相当于在红黑树中插入一个节点。
在 32 位的系统上,线性地址空间可达到 4GB,这 4GB 一般按照 3:1 的比例进行分配,也就是说用户进程享有前 3GB 线性地址空间,而内核独享最后 1GB 线性地址空间。由于虚拟内存的引入,每个进程都可拥有 3GB 的虚拟内存,并且用户进程之间的地址空间是互不可见、
互不影响的,也就是说即使两个进程对同一个地址进行操作,也不会产生问题。在前面介绍的一些分配内存的途径中,无论是伙伴系统中分配页的函数,还是 slab 分配器中分配对象的函数,它们都会尽量快速地响应内核的分配请求,将相应的内存提交给内核使用,而内核对待用户空间显然不能如此。用户空间动态申请内存时往往只是获得一块线性地址的使用
权,而并没有将这块线性地址区域与实际的物理内存对应上,只有当用空间真正操作申请的内存时,才会触发一次缺页异常,这时内核才会分配实际的物理内存给用户空间。
用户进程的虚拟地址空间包含了若干区域,这些区域的分布方式是特定于体系结构的,不过
所有的方式都包含下列成分:
可执行文件的二进制代码,也就是程序的代码段
存储全局变量的数据段
用于保存局部变量和实现函数调用的栈
环境变量和命令行参数
程序使用的动态库的代码
用于映射文件内容的区域
总结
实际上设备系统那部分已经在虚拟文件系统模块图上面有所体现了,因此就不单独拿出来说了。
linux内核是非常值得一学的,内部设计到丰富的知识积累。对于我们这种急求进阶的人士多多研读其中的实现手段是有很大帮助的。