日期:2014-05-16  浏览次数:20816 次

深入理解Linux内核--内核同步(阅读笔记)(原创)

深入理解Linux内核--内核同步(阅读笔记)(原创)


?

由 王宇 原创并发布

?

??????? 每当读到书中的这一章节时,都使我产生放弃的想法。原因是这章中的内容是我认为最复杂,最难以理解 的一章(对充分理解Linux操作系统,也是非常重要 的一部分)。我是将中断(包括可延迟函数)、信号、系统调用、进程、内存、文件系统,这些部分的内容阅读完成后,在阅读此章的,所以我推荐和我有同样感受的读者,在没有搞清楚其他章节的内容时,可以先放弃这章的阅读。

?

第五章内核同步

你可以把内核看作是不断对请求进行响应的服务器,这些请求可能来自在CPU上执行的进程,也可能来自发出中断请求的外部设备。我们用这个类比来强调内核的各个部分并不是严格按照顺序依次执行的,而是采用交错执行的方式。因此,这些请求可能要求竞争条件 ,而我们必须采用适当的同步机制对这种情况进行控制。

1、内核如何为不同的请求提供服务

??? 我们把内核看作必须满足两种请求的侍者:一种请求来自顾客,另一种轻轻来自数量有限的几个不同的老板。策略

??? ??? 1、老板提出请求时,如果侍者正空闲,则侍者开始为老板服务

??? ??? 2、如果老板提出请求时侍者正在为顾客服务,那么侍者停止为顾客服务,开始为老板服务。

??? ??? 3、如果一个老板提出请求时侍者正在为另一个老板服务,那么侍者停止为第一个老板提供服务,而开始为第二个老板服务,服务完毕再继续为第一个老板服务。

??? ??? 4、一个老板可能命令侍者停止正在为顾客提供的服务。侍者在完成对老板最近请求的服务之后,可能会暂时不理会原来的顾客而去为新选中的顾客服务


??? 侍者提供的服务对应于CPU处于内核态时所执行的代码。如果CPU在用户态执行,则侍者被认为处于空闲状态。

??? 老板的请求相当于中断,而顾客的请求相当于用户态进程发出的系统调用或异常


??? [1]内核抢占

??? ??? 如果进程执行内核函数时,即它的内核态运行时,允许发生内核切换(被替换的进程是正执行内核函数的进程),这个内核就是抢占的。

??? ??? 抢占内核的主要特点 是:一个在内核态运行的进程,可能在执行内核函数期间被另外一个进程取代

??? ??? 使内核可抢占的目的是减少用户态进程的分配延迟,即从进程变为可执行状态到它实际开始运行之间的时间间隔。内核抢占对执行及时被调度的任务(如:电影播放器)的进程确实是有好处的,因为它降低了这种进程被另一个运行在内核态的进程延迟的风险

??? ??? 以下原则告诉我们:只有内核正在执行异常处理程序(尤其是系统调用),而且内核抢占没有被显式地禁用时,才可能抢占内核。

??? ??? thread_info描述符的preempt_count字段大于0时

??? ??? 1、内核正在执行中断服务例程

??? ??? 2、可延迟函数被禁止(当内核正在执行软中断或tasklet时经常如此)

??? ??? 3、通过把抢占计数器设置为正数而显式地禁用内核抢占

??? ??? 内核抢占会引起不容忽视的开销。因此Linux2.6独具特色地允许用户在编译内核时通过设置选项来禁用或启用内核抢占

??? [2]什么时候同步是必需的

??? ??? 当计算的结果依赖于两个或两个以上的交叉内核控制路径的嵌套方式时,可能出现竞争条件。临界区是一段代码,在其他的内核控制路径能够进入临界区前,进入临界区的内核控制路径必须全部执行完这段代码。

??? ??? 交叉内核控制路径使内核开发者的工作变得复杂:他们必须特别小心地识别出异常处理程序、中断处理程序、可延迟函数和内核线程中的临界区。一旦临界区被确定,就必须对其采用适当的保护措施,以确保在任何时刻只有一个内核控制路径处于临界区。

??? ??? 如果是单CPU的系统,可以采取访问共享数据结构时关闭中断的方式来实现临界区,因为只有在开中断的情况下,才可能发生内核控制路径的嵌套。

??? ??? 另外,如果相同的数据结构仅被系统调用服务例程所访问,而且系统中只有一个CPU,就可以非常简单地通过在访问共享数据结构时禁用内核抢占功能来实现临界区。

??? ??? 正如你们所预料的,在多处理器系统中,情况要复杂得多。由于许多CPU可能同时执行内核路径,因此内核开发者不能假设只要禁用内核抢占功能,而且中断、异常和软中断处理程序都没有访问过该数据结构,就能保证这个数据结构能够安全地被访问。

??? [3]什么时候同步是不必需的

??? ??? 所有的中断处理程序响应来自PIC的中断并禁用IRQ线。此外,在中断处理程序结束之前,不允许产生相同的中断事件

??? ??? 中断处理程序、软中断和tasklet既不可以被抢占也不能被阻塞,所以它们不可能长时间处于挂起状态。在最坏的情况下,它们的执行将有轻微的延迟,因此在其执行的过程中可能发生其他的中断(内核控制路径的嵌套执行)

??? ??? 执行中断处理的内核控制路径不能被执行可延迟函数或系统调用服务例程的内核控制路径中断

??? ??? 软中断和tasklet不能在一个给定的CPU上交错执行

??? ??? 同一个tasklet不可能同时在几个CPU上执行。

??? ??? 简化的例子:(???)

??? ??? ??? 中断处理程序和tasklet不必编写成可重入的函数

??? ??? ??? 仅被软中断和tasklet访问的每CPU变量不需要同步

??? ??? ??? 仅被一种tasklet访问的数据结构不需要同步

2、同步原语

??? 我们考察一下在避免共享数据之间的竞争条件时,内核控制路径是如何交错执行的。**表5-2列出了Linux内核使用的同步技术。“适用范围”一栏表示同步技术是适用于系统中的所有CPU还是单个CPU

??? [1]每CPU变量

??? ??? 事实上每一种显式的同步原语都有不容忽视的性能开销。

??? ??? 最简单也是最重要的同步技术包括把内核变量声明为每CPU变量(per-cpuvariable)每CPU变量主要是数据结构的数组,系统的每个CPU对应数组的一个元素。

??? ??? 一个CPU不应该访问与其他CPU对应的数组元素,另外,它可以随意读或修改它自己的元素而不用担心出现竞争条件,因为它是唯一有资格这么做的CPU,但是,这也意味着每CPU变量基本上只能在特殊情况下使用,也就是当它确定在系统的CPU上的数据在逻辑上是独立的时候。