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

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

八、完成量

下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等待另一个执行单元完成某事后方可执行,它是一种轻量级机制。事实上,它即是为了完成进程间的同步而设计的,故而仅仅提供了代替同步信号量的一种解决方法,初值被初始化为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机制的相关分析,感兴趣的读者可继续阅读后一篇博文。由于笔者水平所限,博文中难免有出错之处,欢迎读者指出,大家相互讨论,共同进步。

转载请注明出处:http://blog.sina.com.cn/huangjiadong19880706

 

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

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

    在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. H5上滑跳转页面

    方法一: jquery方法 movePage($('body')); function movePage(dom) { var startY, moveY, moveSpave; dom.on(&qu ...

  2. python Tensorflow 实现图像的卷积处理

    1.convolution.py import numpy as np from sklearn.datasets import load_sample_images import tensorflo ...

  3. [SCOI2016]背单词——trie树相关

    题目描述 Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“.这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的: ...

  4. JQ的几组API辨析:

    1.插入: Jq的插入一共有八个API可供选择,结果相类似,下面将以例子,简单明了的介绍下用法: <select name="one" multiple="mult ...

  5. Signal & Slot in Qt

    Try your best to provide an mechanism to implement what you want. 1. All is generated by QT Framewor ...

  6. 阿里,百度,腾讯招聘 Java 程序员的技术标准

    阿里巴巴篇 扎实的计算机专业基础,包括算法和数据结构,操作系统,计算机网络,计算机体系结构,数据库等 具有扎实的Java编程基础,理解IO.多线程等基础框架 熟练使用Linux系统的常用命令及shel ...

  7. Docker的一些常用命令

    # systemctl start docker //启动docker # systemctl restart docker //重启docker # systemctl enable docker ...

  8. .NET的那些事儿(9)——C# 2.0 中用iTextSharp制作PDF(基础篇) .

    该文主要介绍如何借助iTextSharp在C# 2.0中制作PDF文件,本文的架构大致按照iTextSharp的操作文档进行翻译,如果需要查看原文,请点击一下链接:http://itextsharp. ...

  9. 五、python小功能记录——打包程序

    使用pyinstaller打包Python程序 安装工具 :pip3 install pyinstaller 在Python程序文件夹上(不点进去)按住shift并且右键,在弹出的选项中点击" ...

  10. 打通版微社区(2):服务器部署MySql数据库 For DZ3.2

    写在前面:单独写部署MySql原因是,我这边的应用数据库都是独立存在的,数据与应用分别部署在不同的服务器.另外我也没有实际部署MySql的经验,特意写一篇日志,张记性.安装MySql参考了http:/ ...