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机制的相关分析,感兴趣的读者可继续阅读后一篇博文。由于笔者水平所限,博文中难免有出错之处,欢迎读者指出,大家相互讨论,共同进步。
Linux内核中锁机制之完成量、互斥量的更多相关文章
- 大话Linux内核中锁机制之完成量、互斥量
大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内 ...
- Linux内核中锁机制之信号量、读写信号量
在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...
- 大话Linux内核中锁机制之信号量、读写信号量
大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...
- Linux内核中锁机制之RCU、大内核锁
在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...
- Linux内核中锁机制之原子操作、自旋锁
很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多 ...
- 大话Linux内核中锁机制之RCU、大内核锁
大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核 ...
- 大话Linux内核中锁机制之原子操作、自旋锁
转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其 ...
- 大话Linux内核中锁机制之原子操作、自旋锁【转】
转自:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实 ...
- Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...
随机推荐
- JavaScript数组与字符串常用方法总结
先来一段代码引子: var str='hello world'; alert(str.charAt());//通过下标查找值: alert(str.indexOf());//通过值查找字符串下标:没有 ...
- Kafka集群环境搭建(2.9.2-0.8.2.2)
Kafka是一个分布式.可分区.可复制的消息系统.Kafka将消息以topic为单位进行归纳:Kafka发布消息的程序称为producer,也叫生产者:Kafka预订topics并消费消息的程序称为c ...
- Mac下如何进行端口转发,方便一系列需要使用80端口进行的调试工作
上篇文章介绍到,可以在本地hosts文件中添加一条记录将微信公众号中的可信域名解析道本地127.0.0.1,但tomcat在Mac下非root权限80端口是启动不了的,所以我们可以利用pfctl端口转 ...
- Win10系统下面的TR1008解决方案
最近为了体验高大上的Win10系统,于是就把本本原来的Win7旗舰版 升级 到了Win10专业版.之后又在本本上部署了cognos,但是在打开Transform导入IQD数据源的时候就报错了,错误和之 ...
- System.Windows.Forms.ListView : Control
#region 程序集 System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ...
- js正则表达式/replace替换变量方法
转自:http://www.blogjava.net/pingpang/archive/2012/08/12/385342.html 1. javascript 正则对象替换创建和用法:/patter ...
- 【C#】C#委托学习
虽然做.NET这行也快2年了,但基础不太好啊,今天看了下委托方面的知识,记录下. 1.委托 总的来说,委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋 ...
- Makefile整理
Makefile里的subst 用法是$(subst FROM,TO,TEXT),即将TEXT中的东西从FROM变为TO Makefile中的字符串处理函数 格式: $(subst <from& ...
- 创建线程安全的单例(ARC或 非ARC)
一:创建 宏 文件 SynthesizeSingleton.h SynthesizeSingleton.h #if __has_feature(objc_arc) // ARC Version #de ...
- 算法笔记_184:历届试题 约数倍数选卡片(Java)
目录 1 问题描述 2 解决方案 1 问题描述 问题描述 闲暇时,福尔摩斯和华生玩一个游戏: 在N张卡片上写有N个整数.两人轮流拿走一张卡片.要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数 ...