在上一篇博文中笔者分析了关于信号量、读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题。

八、完成量

下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等待另一个执行单元完成某事后方可执行,它是一种轻量级机制。事实上,它即是为了完成进程间的同步而设计的,故而仅仅提供了代替同步信号量的一种解决方法,初值被初始化为0。它在include\linux\completion.h定义。

如图8.1所示,对于执行单元A而言,如果执行单元B不执行complete函数,执行单元A就会因为申请不到进程而睡眠,直至complete函数在执行单元B中被调用,所以执行代码b前必须等到执行单元B执行完代码c。这一点同信号量同步的机制类似,只是调用的函数不同而已。

图8.1      完成量的使用示例

下面笔者将讨论下它的实现机制。笔者从内核中找出了相关的源码,依次如下图8.2至图8.5所示。由于图8.5中的do_wait_for_common函数实现较多,故给出的是删节后的函数大概框架图。

图8.2      完成量的结构体定义

图8.2展示了完成量的结构体定义,done变量是完成量要保护的对象,wait则是申请完成量的进程的等待队列。从图8.3中我们可以看出当初始化完成量变量时,done变量被初始化成0,对比利用信号量实现同步的内容,可以发现它们是相一致的。

图8.3      完成量的内核源码

配合图8.1中的完成量源码,我们发现现在对于执行单元A,它现在执行完代码a后调用wait_for_common函数,这个函数的源码实现如图8.5所示。可以看出,它其实即是对done变量作判断,若done变量没有大于0,则它一直处于while循环中。此时,若是执行单元B执行完代码c后,执行complete函数,此时,观察图8.4中的complete函数实现,可以发现它对done变量值增1。此时,wait_for_common函数便会退出while循环,同时将done值减1,以表示申请完成量成功。


图8.4      完成量的内核源码

图8.5      完成量的内核源码

至此,关于完成量的内容即讨论到此,总体来说,完成量的内容还是较为简单的。后续笔者将会讨论有关互斥量的使用和实现。

九、互斥量

下面笔者将讨论互斥量的相关内容。首先互斥量的提出主要是由于进程在使用信号量后开销较大。互斥量提供了两种机制,包括经典互斥量和实时互斥量两种,依次在文件include\linux\mutex.h (经典互斥量)和include\linux\rtmutex.h (实时互斥量)中定义,下面我们来看下它的一些具体内容。

首先讨论关于经典互斥量的内容,它的结构体定义如图9.1所示。结构体中我们重点关注count、wait_lock、wait_list等变量。和读写信号量的定义相类似。

图9.1      经典互斥量的结构体定义

另外,关于它的用法和先前的一些锁机制其实都差不多,具体函数即把加解锁的函数换成mutex_lock和mutex_unlock等,而它的内部的源码实现是建立上原子锁和自旋锁的相关机制之上,同时还有一些关于队列维护的一些内容,这些内容,我们可从它的结构体定义即可简单看出,这里不太深入讨论了,感兴趣的话可以看上面提示的两个文件。

下面我们来介绍实时互斥锁的内容,在介绍这个内容之前,我们先来看一个有趣的问题,就是无限制优先级反转问题,这个问题曾经引起了美国的火星探测器的故障(后续介绍)。

下面具体讨论到底什么是无限制优先级反转问题,总体来说,图9.2至图9.7展示了整个问题发生的过程。首先当前有一个执行单元C获取了一个互斥量,正在所保护的临界区中执行,且不打算在短时间内推出。此时,系统中“跑来”执行单元A申请这个互斥量,由于执行单元C正在使用这个互斥量,故A只能等待C执行完,尽管A的优先级远高于C(多么无奈)。这时候,又冒出一个执行单元B,它的优先级介于A和C之间。由于系统的一个中断的发生,导致执行单元B直接抢占C进程而开始执行互斥量所在的临界区。但这种情况实际上也抢占了执行单元A,尽管执行单元A的优先级高于执行单元B(A就是个悲剧)。如果执行单元B继续执行,那么执行单元A将等待更长的时间,因为执行单元C被执行单元B抢占,所以它也只能等待执行单元B完成其操作。因此看起来就像执行单元B的优先级高于执行单元A一样。这种情况就是我们所说的无限制优先级反转问题。

图9.2  执行单元C访问临界区                                         图9.3  执行单元A等待C完成其操作

图9.4  B抢占C而执行临界区                                              图9.5  B在临界区中执行

图9.6  B执行完后C继续执行临界区                                      图9.7  A最终执行临界区

下面简单提及下美国火星探测器的故障问题:1997年的美国的火星探测器(探测器使用的是Vxworks系统)遇到一个优先级反转问题引起的故障。首先火星探测器有一个信息总线,有一个高优先级的总线任务负责总线数据的存取,访问总线都需要通过一个互斥锁(共享资源出现了);还有一个低优先级的,运行不是很频繁的气象搜集任务,它需要对总线写数据,也就同样需要访问互斥锁;最后还有一个中优先级的通信任务,它的运行时间比较长。平常这个系统运行毫无问题,但是有一天,在气象任务获得互斥锁往总线写数据的时候,一个中断发生导致通信任务被调度就绪,通信任务抢占了低优先级的气象任务,而无巧不成书的是,此时高优先级的总线任务正在等待气象任务写完数据归还互斥锁,但是由于通信任务抢占了CPU并且运行时间比较长,导致气象任务得不到CPU时间也无法释放互斥锁,从而使本来是高优先级的总线任务也无法执行,总线任务无法及时执行的后果被探路者认为是一个严重错误,最后就是整个系统被重启。事实上,Vxworks系统允许优先级继承,然而遗憾的是工程师们将这个功能给停止了,从而使火星探测器就此悲剧了。

由于无限制优先级反转问题无法使用经典互斥量来解决,理由是经典互斥量中的队列并没有实现将等待进程按优先级排队。实时互斥量就此因运而生,我们们可以简单看下它的结构体定义,如图9.8所示。

图9.8      实时互斥量的结构体定义

既然经典互斥量不能解决将等待进程按优先级排队问题,因此对于实时互斥量,它的实现关键即是wait_list的实现,看此变量的定义类型为plist_head,便可一目了然。在plist_head链表操作数,它即采用了优先级继承关系将等待进程队列按进程优先级方式排队。这里优先级继承表示为如果高优先级的进程阻塞在互斥量上,该互斥量当前由低互斥量拥有,那么将低优先级的临时提升到高优先级。

至此,关于互斥量的内容即讨论到此,经过上述的讨论,相信读者对于互斥量的内容有了比较好的了解,后续《大话Linux内核中锁机制之RCU、大内核锁》将会重点给出有关RCU机制的相关分析,感兴趣的读者可继续阅读后一篇博文。由于笔者水平所限,博文中难免有出错之处,欢迎读者指出,大家相互讨论,共同进步。

Linux内核中锁机制之完成量、互斥量的更多相关文章

  1. 大话Linux内核中锁机制之完成量、互斥量

    大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内 ...

  2. Linux内核中锁机制之信号量、读写信号量

    在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...

  3. 大话Linux内核中锁机制之信号量、读写信号量

    大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...

  4. Linux内核中锁机制之RCU、大内核锁

    在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...

  5. Linux内核中锁机制之原子操作、自旋锁

    很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多 ...

  6. 大话Linux内核中锁机制之RCU、大内核锁

    大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核 ...

  7. 大话Linux内核中锁机制之原子操作、自旋锁

    转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其 ...

  8. 大话Linux内核中锁机制之原子操作、自旋锁【转】

    转自:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实 ...

  9. Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁

    在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...

随机推荐

  1. Reorder List leetcode java

    题目: Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must d ...

  2. JavaScript快速检测浏览器对CSS3特性的支持情况

    项目中使用动画效果在IE9下不支持,所以写了个判断浏览器是否支持动画的函数,进而扩展到下面判断浏览器支持任一特定CSS3属性的函数. function supportAnimation(){ var ...

  3. Spark RDD关联操作小结

    前言 Spark的rdd之间的关系需要通过一些特定的操作来实现, 操作比较多也,特别是一堆JOIN也挺容易让人产生混乱的. 因此做了下小结梳理一下. 准备数据 var rdd1 = sc.makeRD ...

  4. 【LeetCode】Path Sum II 二叉树递归

    Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given su ...

  5. Java--解压缩zip包

    Test.java import java.io.IOException; public class Test { public static void main(String[] args) thr ...

  6. 盘点linux系统中的12条性能调优命令。

    导读 性能调优一直是运维工程师最重要的工作之一,如果您所在的生产环境中遇到了系统响应速度慢,硬盘IO吞吐量异常,数据处理速度低于预期值的情况,又或者如CPU.内存.硬盘.网络等系统资源长期处于耗尽的状 ...

  7. Mongo读书笔记2 -- 数据类型

      Mongo有多种类型的collection, 默认的是随着document 数目的增多自动增大; 还有一些collection被称为capped collection, 只能包含固定数目的docu ...

  8. 正则 js截取时间

    项目中要把时间截取,只要年月日,不要时分秒,于是 /\d{4}-\d{1,2}-\d{1,2}/g.exec("2012-6-18 00:00:00") 或者另一种 var dat ...

  9. [Backbone] Verying Views

    Below we have our AppointmentsView instance rendering and then taking the rendered HTML and insertin ...

  10. 剑指offer面试题12-打印1到最大的n位数

    题目: 输入一个数字n,按顺序打印出从1最大的n位十进制数.比方输入3,则打印出1.2.3最大的三位数即999 这道题的主要陷阱就在大数的处理,仅仅要将这个考虑进去,用字符串来表示.就好说了. 那差点 ...