Linux内核设计与实现LKD - 美 - Love - 机械工业出版社

Linux内核设计与实现LKD - 美 - Love - 机械工业出版社

Linux内核简介

Linux内核有两种,稳定的和处于开发中的。内核一般用点号分三段,主版本号.从版本号.修订版本号,如2.26.0。从版本号为偶数则为稳定版,奇数则为开发版。

从内核出发

内核源码一般位于/usr/src/linux目录,或者/usr/src/kernel目录中,没有的可以自己安装。参考安装Linux内核源码部分文字。

ls默认按字典序纵向排序。

进程管理

现代操作系统中,进程提供了两种虚拟机制:虚拟处理器和虚拟内存。线程间共享虚拟内存,但拥有各自的虚拟处理器。

进程描述符:内核把进程都放在一个双向循环链表中,叫做任务队列。链表中的每一项都是一个结构体,称为进程描述符。

进程家族树:系统中所有的进程都是PID为1的init进程的后代,内核在启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行相关程序。

进程创建:Unix系统进程创建很特别,许多其他系统都提供了spawn产生一个新进程,而Unix则使用fork复刻一个进程并使用exec执行目标程序,其效果跟spawn相同。

linux的fork使用copy on write写时复制,调用fork时内核并不马上复制整个进程地址空间,而是与父进程共享内存空间,而是但写入的时候才会复制。COW这种延迟复制的方式十分有效,因此这个优化是很重要的。

线程在Linux中的实现

Linux实现线程的机制非常独特,它并没有线程这个概念,Linux把所有线程当作进程来实现,这些线程仅仅被视为一个与其他进程共享某些资源的进程。创建线程时和fork一样,底层也是clone,但创建线程需要指明共享的资源,这样,父进程和紫禁城就是所谓的线程了。

线程的实现Linux和Windows、Solaris的实现非常差异十分巨大。这些系统都在内核中提供了专门支持进程的机制,这些系统常常称线程为“轻量级进程”。而对于Linux来说,线程就是进程,它知识进程间共享资源的手段,比较Linux的进程已经够轻量了。

进程调度

调度程序的一个基本工作是,在一组处于可运行状态的进程中选择一个来执行。

多任务系统分为两类:抢占式和非抢占式。现代操作系统都是使用抢占式,这种模式下,由调度程序决定什么时候停止一个程序的运行。进程在被抢占前能够运行的时间是预先设置好的,叫时间片。而非抢占式的系统,除非进程自己主动停止运行,否则他会一直执行。

时间片:时间片是一个数值,但不是一个固定值,它表明进程在被抢占前所能运行的时间。

Linux的CFS调度器:Linux的CFS调度器采用平分处理器时间的策略,当系统中只有两个进程在跑时,如文本编辑器和视频解码器,它们各自得到50%的处理器时间,但文本编辑器大部分时间在等待用户输入,所以每次执行完用户输入后都处于睡眠状态。而视频处理器每次都消耗了所有的CPU时间,所以CFS发现文本编辑器的CPU消耗少于承诺给他的50%,所以CFS会优先调度文本编辑器进行运行。

系统调用

内核数据结构

内核常用的内建数据结构:链表、队列、映射、二叉树。

二叉树有:二叉搜索树、平衡二叉搜索树(所有叶子节点深度差不超过1)、自平衡二叉搜索树(所有操作都试图维持平衡的二叉搜索树,如红黑树)。红黑树是Linux最主要的二叉树数据结构。

中断和中断处理

在大多数体系结构中,中断就是通过电信号给处理器的特定管脚发送一个信号。处理器会立刻停止它在做的事情,然后跳到内存中预定义的位置开始执行那里的代码。

内核同步介绍

内核同步方法

原子操作是其他同步方法的基石。原子操作可以保证指令以原子的方式执行,即执行过程不被打断。

内核提供了两组原子操作接口,其中原子操作的整数定义为automic_t,操作有automic_set()、automic_add()等。

原子性并不能保证顺序性,顺序性确保了即使两条或多条指令出现在独立的执行线程中,它们本该的执行顺序却依然要保持,顺序性是通过内存屏障来实现的。

Linux内核中最常见的锁是自旋锁,使用spin_lock_irq()方法。

信号量:计数信号量和二值信号量。二值信号量在任意一个时刻仅允许一个锁持有者,也成为互斥信号量。计数信号量允许多个锁持有者,不能用来进行强制互斥,内核使用它的机会不过。

顺序性和内存屏障:编译器为了提高效率,可能会对读和写重新排序。实现顺序性,CPU有机器指令可以确保顺序要求,或者也可以指示编译器不要对给定的周围的指令序列进行重排序。这些确保顺序的指令称为屏障。

rmb()指令提供了一个“读”内存屏障,它确保在rmb()之前的载入动作不会重排到rmb()调用之后,rmb()之后的载入动作不会重排到rmb()之前。

定时器和时间管理

时间管理在内核中占有非常重要的地位,内核中有大量的函数都是基于时间驱动的,例如调度程序和屏幕刷新的动作都需要定期执行。

周期性产生的事件都是由系统定时器驱动的,如每10ms一次,定时器是一种可编程硬件芯片,它能以固定频率产生中断,该中断就是定时器中断。

Linux中利用时钟中断来工作的程序有:

  • 更新系统运行时间
  • 进程时间片调度

内存管理

内核把物理页作为内存管理的基本单位。从虚拟内存的角度看,页就是最小单位。大多数32位系统支持4KB的页,而64位系统支持8KB的页。

虚拟文件系统

lo4qp9.png

块I/O层

块设备和字符设备:字符设备按照字节流的方式被有序访问,而块设备是以无序、随机的方式访问并且有固定大小。

块设备最小的可寻址单元是扇区,常见的扇区是512字节。然而,块是文件系统的一种抽象,只能基于块来访问文件系统,内核执行的所有磁盘操作都是基于块进行的,块的大小是扇区的2的整数倍,而且不能大于页的大小,所以块的大小一般是512B,1KB或4KB。

进程空间地址

用户空间中进程的内存,称为进程地址空间。

页高速缓存和页回写

页高速缓存指的是把磁盘中的物理数据缓存到物理内存中。

写缓存:对于写入到磁盘的操作,使用什么缓存策略呢?有三种策略

  • 不缓存:写操作时,直接跳过缓存,写入磁盘,同时使缓存的数据失效。这种策略很少使用。
  • 写操作自动更新内存缓存:这种称为写透缓存。
  • 回写:这是Linux采用的策略,写操作直接写到缓存中,不会更新磁盘,并标记该缓存也为脏页,加入脏页列表。然后由一个进程(回写进程)周期将脏页列表中的页写入磁盘,最后清理脏页标志。这里“脏页”指的是磁盘中的数据,而不是内存中的数据。

设备与模块

调试

内核调试。

可移植性

数据类型等相关可移植性代码

补丁、开发和社区

内核编码风格和如何共享内核代码

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦