Linux内核精析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 Linux的基本组成

1.2.1 进程

操作系统中的进程定义为程序执行的一个实例,每个进程都在CPU的虚拟内存中分配地址空间,并且每个进程的地址空间都是相互独立的。进程地址空间由允许进程使用的全部线性地址组成,每个进程所看到的线性地址集合是不同的,一个进程使用的地址与另外一个进程使用的地址之间没有关系,内核可以通过增加或删除某些线性区间来动态修改进程的地址空间。

CPU既可以在用户模式下运行,也可以在内核模式下运行,当某个程序在用户模式下运行时,不能直接访问内核中的数据结构或程序,但是,当程序运行在内核模式时,可以访问用户态的数据。对于某个执行的程序,多数的时间都处于用户模式,只有在需要内核提供服务时才会切换到内核模式,当内核满足了用户程序的需求后,再次返回到用户模式。

Linux系统中传统调度器对进程分别计算时间片,在进程的所有时间片用尽时,需要重新计算,现在的调度器只考虑进程的等待时间,也就是进程在就绪队列中已等待的时间。调度器的原理是按所分配的计算能力,向进程提供最大的公正性;每次调用调度器时,会选择等待时间最长的进程,把CPU提供给该进程,这样,进程的不公平性不会累积,不公平会均匀地分布到系统的所有进程。

1.2.2 进程间通信

进程之间需要相互协作,在Linux系统中存在各种形式的进程间通信。进程间通信通常的实现方式有通过信号量机制与其他进程进行同步、向其他进程发送消息、从其他进程接收消息以及与其他进程共享一段内存区。IPC数据结构是在进程请求IPC资源(信号量、消息队列或者共享内存)时动态创建的,三种进程间通信的共同点是都使用了全系统范围的资源,该资源可由几个进程同时共享。由于一个进程可能需要同类型的多个IPC资源,因此每个新资源都使用一个IPC关键字来标识。

IPC信号量与内核信号量非常类似,除此之外与内核信号量没有任何关系,信号量不作为用于支持原子操作预定义操作的简单类型变量,而是一整套信号量,可以允许多个操作同时进行。

消息队列是指进程彼此之间可以通过IPC消息进行通信,进程产生的每条消息都被发送到一个IPC消息队列中,该消息一直存放在队列中直到另一个进程将该消息读走。产生消息并将其写入队列的进程称为发送方,其他进程则从消息队列中获取该消息。该消息包含消息正文和一个整数,数字用于标识该消息,接收方可以根据数字搜索消息,消息读取后,内核从队列中删除该消息,只能有一个进程接收一条给定的消息。

共享内存是允许两个或多个进程把公共数据放入一个共享内存区来进行访问,如果进程要访问存放在该共享内存区的数据,必须在自己的地址空间增加一个新的内存区,该内存将映射与共享内存区相关的页框,这样的页框可以方便地由内核通过请求调页进程处理。

1.2.3 内存管理

内存管理是内核中最重要同时也是最复杂的部分,需要处理器和内核之间的协作,内核在启动时通过调用start_kernel()函数实现内存结构的初始化工作,之后内存管理的工作交由伙伴系统算法承担。伙伴算法采用页框作为基本内存区,适合于大块内存的请求,但是当有小内存区的请求时(几十或几百字节),分配一整个页框是一种浪费,因此引入了新的数据结构来描述在同一页框内分配小内存区。

伙伴系统支持按页分配内存,如果需要分配较小的空间,分配一个或多个页框的空间,这样非常浪费空间,比较好的解决方法是将页框拆分成较小的单位。Linux内核采用了一种称为“slab”的缓冲分配和管理方法,通过建立slab缓冲,内核能够储备一些对象,供后续使用。slab分配器将释放的内存块保存在一个内部列表中,而不是立刻返回给伙伴系统,这样内核不必使用伙伴系统算法,缩短了处理的时间;其次,由于该内存块仍然是新的,其驻留在CPU高速缓存的概率较高。

由于slab过于复杂,并且本身的管理结构也需要占用过多的内存,同时效率也相对较低,因此出现了slub分配器,slub分配器提供与slab一致的接口,不仅简化了slab分配器,而且提供了更好的性能。

1.2.4 设备驱动

根据设备驱动程序的基本特性,设备文件可以分为块设备与字符设备,块设备的数据可以随机访问,如硬盘、CD-ROM驱动器等,对于字符设备的数据则不可以随机访问;设备文件是存放在文件系统中的实际文件,其索引节点不包含指向磁盘上数据块的指针,但是必须包含硬件设备的标识符,对应字符或块设备文件。

由于块设备层的设计导致需要持续地调整块设备的速率及工作方式,因此块设备驱动程序要比字符设备复杂得多。块设备的主要特点是CPU和总线读/写数据所耗费的时间与磁盘硬件的速度不匹配;块设备的平均访问时间长,这是因为磁盘控制器必须在磁盘表面将磁头移动到存储数据的准确位置。

驱动程序通过一组固定的接口与内核代码通信,而扩展设备则通过设备驱动程序处理,因此扩展设备/驱动程序对内核的代码没有影响;内核代码与总线驱动程序的关系比与具体设备驱动程序的关系更为密切。此外,由于不同总线系统之间使用的硬件技术差异较大,因此总线驱动程序向相关的设备驱动程序提供功能和选项的方式不存在标准的接口。

1.2.5 中断

Linux内核中的中断通常分为同步中断和异步中断,同步中断是CPU本身在执行程序过程中由CPU控制单元产生的,而异步中断则是由其他硬件设备随机产生的;不同设备对应的中断不同,每个中断通过一个唯一的数字标识,因此使得内核能够对中断进行区别,这样内核能给不同的中断提供不同的中断处理程序。

1.2.6 时钟

时钟中断源能周期地向CPU发出中断,内核在中断处的例程中检查当前进程时间片是否到期,为调度器提供时钟源。此外,内核根据RTC获取到起始时间后,依此来维护内核时间,由于RTC计时的单位是秒,为了获取精确的时间,内核启动时从RTC中读取起始时间,之后在每一次时钟中断时,利用起始时间加上中断周期,可以把时钟精确到毫秒的级别。后来又出现了TSC(Time Stamp Counter,是CPU的一个寄存器),该寄存器随着CPU时钟周期的递增加1,如果当前的CPU主频为1GHz,则该寄存器每纳秒加1。内核以RTC为基础,每次时钟中断通过汇编指令rdtsc读取TSC,内核在时钟中断中通过TSC能够计算出两次时钟中断间流逝的精确时间,这样时间能够精确到纳秒的级别。

1.2.7 文件系统

每种操作系统至少都有一种“标准文件系统”,提供了某些功能可以可靠地执行特定的任务,Linux附带的ext2/3文件系统是一种标准文件系统,该文件系统经证实是健壮的,同时Linux还提供了备选的方案。

为了支持各种本机文件系统,并且同时允许访问其他操作系统的文件,Linux内核在用户进程和文件系统实现之间提供了一个抽象层,称为虚拟文件系统(VFS,Virtual File System),一方面用来提供一种文件、目录及其他对象操作的统一方法,另一方面能够与各种方法给出的具体文件系统的实现达成妥协。如图1-2所示为虚拟文件系统的示意图。

图1-2 虚拟文件系统的示意图

1.2.8 内核模块

内核中的模块是一种向Linux内核添加设备驱动、文件系统及其他组件的有效方法。模块无须重启系统,通过使用模块,内核的开发者可以将实验性的代码打包到模块,模块可以卸载也可以重新安装,可以无缝地插入内核。在模块需要卸载时,可以与内核剩余部分相关联。