Linux内核系统篇——内核的5个重要子系统?

为了让大家建立知识体系,零散的讲可能会让读者觉得有点难以接受,晦涩 。所以本文呢 , 整体的讲解下内核重要的5个子系统 。首先一张熟悉的图来说明GNU/linux的基本体系结构:
体系的上部分是用户(或应用程序)空间 , 这是用户应用程序执行的地方 。用户空间之下是内核空间,Linux 内核正是位于这里 。
Linux 内核可以进一步划分成 3 层:最上面是系统调用接口,用户程序通过软件中断后,调用系统内核提供的功能,这个在用户空间和内核提供的服务之间的接口称为系统调用,它实现了一些基本的功能,例如 read 和 write,这些都是系统调用;
系统调用接口之下是内核代码,可以更精确地定义为独立于体系结构的内核代码,这些代码是 Linux 所支持的所有处理器体系结构所通用的;
内核代码之下是依赖于体系结构的代码,构成了通常称为 BSP(Board)的部分,这些代码用作给定体系结构的处理器和特定于平台的代码,依赖特定硬件结构 。
然后我们来了解下Linux内核的五个子系统:
Linux内核主要由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS)、网络接口(NET)和进程间通信(IPC)5个子系统组成,每个都很重要,缺一不可 。如下图所示 。
Linux内核的组成部分与关系
进程调度
进程调度控制系统中的多个进程对CPU的访问 , 使得多个进程能在CPU中“微观串行,宏观并行”地执行 。进程调度处于系统的中心位置 , 内核中其他的子系统都依赖它,因为每个子系统都需要挂起或恢复进程 。
Linux进程状态转换
如上图所示,Linux的进程在几个状态间进行切换 。在设备驱动编程中,当请求的资源不能得到满足时 , 驱动一般会调度其他进程执行 , 并使本进程进入睡眠状态,直到它请求的资源被释放,才会被唤醒而进入就绪态 。睡眠分成可被打断的睡眠和不可被打断的睡眠,两者的区别在于可被打断的睡眠在收到信号的时候会醒 。
在设备驱动编程中,当请求的资源不能得到满足时,驱动一般会调度其他进程执行 , 其对应进程进入睡眠状态,直到它请求的资源被释放,才会被唤醒而进入就绪态 。设备驱动中,如果需要几个并发执行的任务,可以启动内核线程,启动内核线程的函数为:
pid_t (int (*fn)(void *), void *arg,long flags);
当用户使用系统提供的库函数进行进程编程,用户可以动态地创建进程,进程之间还有等待,互斥等操作 , 这些操作都是由linux内核来实现的 。linux内核通过进程管理子系统实现了进程有关的操作 , 在linux系统上,所有的计算工作都是通过进程表现的 , 进程可以是短期的(执行一个命令) , 也可以是长期的(一种网络服务) 。linux系统是一种动态系统,通过进程管理能够适应不断变化的计算需求 。

Linux内核系统篇——内核的5个重要子系统?

文章插图
在用户空间 , 进程是由进程标示符(PID)表示的 。从用户角度看,一个PID是一个数字值,可以唯一标识一个进程,一个PID值在进程的整个生命周期中不会更改,但是PID可以在进程销毁后被重新使用 。创建进程可以使用几种方式,可以创建一个新的进程,也可以创建当前进程的子进程 。
在linux内核空间,每个进程都有一个独立的数据结构,用来保存该进程的ID、优先级、地址的空间等信息,这个结构也被称做进程控制块(Block) 。所谓的进程管理就是对进程控制块的管理 。
linux的进程是通过fork()函数系统调用产生的 。调用fork()的进程叫做父进程,生成的进程叫做子进程 。子进程被创建的时候,除了进程ID外,其它数据结构与父进程完全一致 。在fork()系统调用创建内存之后,子进程马上被加入内核的进程调试队列,然后使用exec()系统调用,把程序的代码加入到子进程的地址空间 , 之后子进程就开始执行自己的代码 。
在一个系统上可以有多个进程,但是一般情况下只有一个CPU,在同一个时刻只能有一个进程在工作,即使有多个CPU,也不可能和进程的数量一样多 。如果让若干的进程都能在CPU上工作,这就是进程管理子系统的工作 。linux内核设计了存放进程队列的结构,在一个系统上会有若干队列,分别存放不同状态的进程 。一个进程可以有若干状态,具体是由操作系统来定义的 , 但是至少包含运行态、就绪态和等待3种状态,内核设计了对应的队列存放对应状态的进程控制块 。
当一个用户进程被加载后,会进入就绪态 , 被加入到就绪态队列,CPU时间被轮转到就绪态队列后 , 切换到进程的代码,进程被执行,当进程的时间片到了以后被换出 。如果进程发生I/O操作也会被提前被换出,并且存放到等待队列 , 当I/O请求返回后,进程又被放入就绪队列 。linux系统对进程队列的管理设计了若干不同的方法 , 主要的目的是提高进程调试的稳定性 。
内存管理
内存管理的主要作用是控制多个进程安全地共享主内存区域 。当CPU提供内存管理单元(MMU)时,Linux内存管理完成为每个进程进行虚拟内存到物理内存的转换 。Linux 2.6引入了对无MMU CPU的支持 。
如下图3所示,一般而言,Linux的每个进程享有4GB的内存空间,0~3GB属于用户空间 , 3~4GB属于内核空间,内核空间对常规内存、I/O设备内存以及高端内存存在不同的处理方式 。
Linux进程地址空间
使用虚拟内存技术的计算机,内存管理的硬件按照分页方式管理内存 。分页方式是把计算机系统的物理内存按照相同大小等分 , 每个内存分片称作内存页,通常内存页大小是4KB 。Linux内核的内存管理子系统管理虚拟内存与物理内存之间的映射关系 , 以及系统可用内存空间 。内存管理要管理的不仅是4KB缓冲区 。Linux提供了对4KB缓冲区的抽象,例如slab分配器 。这种内存管理模式使用4KB缓冲区为基数 , 然后从中分配结构 , 并跟踪内存页使用情况,比如哪些内存页是满的 , 哪些页面没有完全使用,哪些页面为空 。这样就允许该模式根据系统需要来动态调整内存使用 。
在支持多用户的系统上,由于内存占用的增大,容易出现物理内存被消耗尽的情况 。为了解决物理内存被耗尽的问题,内存管理子系统规定页面可以移出内存并放入磁盘中,这个过程称为交换 。内存管理的源代码可以在./linux/mm中找到 。
虚拟文件系统
如下图4所示,Linux虚拟文件系统(VFS)隐藏各种了硬件的具体细节,为所有的设备提供了统一的接口 。而且,它独立于各个具体的文件系统,是对各种文件系统的一个抽象,它使用超级块super block存放文件系统相关信息,使用索引节点inode存放文件的物理信息,使用目录项存放文件的逻辑信息 。
Linux文件系统
在不同格式的文件分区上,程序都可以正确地读写文件,并且结果是一样的 。有时在使用linux系统的时候发现,可以在不同类型的文件分区内直接复制文件,对应用程序来说 , 并不知道文件系统的类型,甚至不知道文件的类型,这就是虚拟文件系统在背后做的工作 。虚拟文件系统屏蔽了不同文件系统间的差异,向用户提供了统一的接口 。
虚拟文件系统,即VFS( File )是Linux内核中的一个软件抽象层 。它通过一些数据结构及其方法向实际的文件系统如ext2,vfat等提供接口机制 。通过使用同一套文件 I/O 系统调用即可对Linux中的任意文件进行操作而无需考虑其所在的具体文件系统格式;更进一步 , 文件操作可以在不同文件系统之间进行 。在linux系统中,一切都可以被看做是文件 。不仅普通的文本文件、目录可以当做文件进行处理,而且字符设备、块设备、套接字等都可以被当做文件进行处理 。这些文件虽然类型不同,但是却使用同一种操作方法 。这也是UNIX/Linux设计的基本哲学之一 。
虚拟文件系统(简称VFS)是实现“一切都是文件”特性的关键,是Linux内核的一个软件层,向用户空间的程序提供文件系统接口;同时提供了内核中的一个抽象功能,允许不同类型的文件系统存在 。VFS可以被理解为一种抽象的接口标准,系统中所有的文件系统不仅依靠VFS共存,也依靠VFS协同工作 。为了能够支持不同的文件系统,VFS定义了所有文件系统都支持的、最基本的一个概念上的接口和数据结构,在实现一个具体的文件系统的时候,需要向VFS提供符合VFS标准的接口和数据结构,不同的文件系统可能在实体概念上有差别 , 但是使用VFS接口时需要和VFS定义的概念保持一致,只有这样,才能实现对用户的文件系统无关性 。VFS隐藏了具体文件系统的操作细节,所以,在VFS这一层以及内核其他部分看来,所有的文件系统都是相同的 。对文件系统访问的系统调用通过VFS软件层处理,VFS根据访问的请求调用不同的文件系统驱动的函数处理用户的请求 。文件系统的代码在访问物理设备的时候 , 需要使用物理设备驱动访问真正的硬件 。
网络接口
网络接口提供了对各种网络标准的存取和各种网络硬件的支持 。如下图所示 , 在Linux中网络接口可分为网络协议和网络驱动程序,网络协议部分负责实现每一种可能的网络传输协议,网络设备驱动程序负责与硬件设备通信,每一种可能的硬件设备都有相应的设备驱动程序 。
Linux网络体系结构
写网络应用程序,使用通过TCP/IP协议与其他机器通信,和前面介绍的内核子系统相似 , 相关的函数也是通过内核的子系统完成的,担当这部分任务的是内核的网络子系统,有时也把这部分代码称为“网络堆栈” 。Linux内核提供了优秀的网络处理能力和功能,这与网络堆栈代码的设计思想是分不开的 , Linux的网络堆栈部分沿袭了传统的层次结构 , 网络数据从用户进程到达实际的网络设备需要四个层次:用户进程,套接字,网络协议,网络设备 。
实际上,在每层里面还可以分为好多层次,数据传输的路径是按照层次来的,不能跨越某个层次 。linux网络子系统对网络层次采用了类似面向对象的设计思路,把需要处理的层次抽象为不同的实体 , 并且定义了实体之间的关系和数据处理流程:
(1)网络协议:网络协议可以理解为一种语言 , 用于网络中不同设备之间的通信,是一种通信的规范 。
(2)套接字:套接字是内核与用户程序的接口,一个套接字对应一个数据连接,并且向用户提供了文件I/O,用户可以像操作文件一样在数据连接上收发数据,具体的协议处理由网络协议部分处理 。套接字是用户使用网络的接口 。
(3)设备接口:设备接口是网络子系统中软件和硬件的接口 , 用户的数据最终是需要通过网络硬件设备发送和接收的,网络设备千差万别,设备驱动也不尽相同,通过设备接口屏蔽了具体设备驱动的差异 。
(4)网络缓冲区:网络缓冲区也称为套接字缓冲区(),是网络子系统中的一个重要结构 。网络传输数据存在许多不定因素,除了物理设备对传输数据的限制(例如MMU),网络受到干扰、丢包、重传等,都会造成数据的不稳定,网络缓冲区通过对网络数据的重新整理,使业务处理的数据包是完整的 。网络缓冲区是内存中的一块缓冲区,是网络系统与内存管理的接口 。
进程通信
进程通信支持提供进程之间的通信 , Linux支持进程间的多种通信机制,包含信号量、共享内存、管道等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递 。
子系统之间的依赖关系
Linux内核的5个组成部分之间的依赖关系如下:
·进程调度与内存管理之间的关系:这两个子系统互相依赖 。在多道程序环境下,程序要运行必须为之创建进程,而创建进程的第一件事情,就是将程序和数据装入内存 。
·进程间通信与内存管理的关系:进程间通信子系统要依赖内存管理支持共享内存通信机制,这种机制允许两个进程除了拥有自己的私有空间,还可以存取共同的内存区域 。
·虚拟文件系统与网络接口之间的关系:虚拟文件系统利用网络接口支持网络文件系统(NFS),也利用内存管理支持设备 。
·内存管理与虚拟文件系统之间的关系:内存管理利用虚拟文件系统支持交换,交换进程(swapd)定期由调度程序调度,这也是内存管理依赖于进程调度的惟一原因 。当一个进程存取的内存映射被换出时,内存管理向文件系统发出请求 , 同时,挂起当前正在运行的进程 。
【Linux内核系统篇——内核的5个重要子系统?】除了这些依赖关系外,内核中的所有子系统还要依赖于一些共同的资源 。这些资源包括所有子系统都用到的例程,如分配和释放内存空间的函数、打印警告或错误信息的函数及系统提供的调试例程等 。