Linux内核中锁机制之信号量、读写信号量
在上一篇博文中笔者分析了关于内存屏障、读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量、读写信号量的内容。
六、信号量
关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程才能执行临界区的代码;不同的是获取不到信号量时,进程不会原地打转而是进入休眠等待状态。它的定义是include\linux\semaphore.h文件中,结构体如图6.1所示。其中的count变量是计数作用,通过使用lock变量实现对count变量的保护,而wait_list则是对申请信号量的进程维护的等待队列。
图6.1 信号量的结构体定义
我们首先看下它是如何使用的,首先定义一个信号量,然后初始化信号量,它包括两种方法。如图6.2所示。方法1:简单的初始化,定义信号量的个数由val决定,实际上val值即是赋给信号量结构体中的count变量;方法2是直接将结构体中count值设置成1,此时信号量可用于实现进程间的互斥量。注意:对于信号量的初始化函数Linux最新版本存在变化,本文所采用的Linux版本已不存在如init_MUTEX和init_MUTEX_LOCKED等初始化函数,同时也更换了名字等,这点读者在阅读的时候需要下,因此笔者建议以后在编程中遇到需要使用信号量的时候尽量采用sema_init(struct semaphore *sem, int val)函数,因为这个函数就目前为止从未发生变化。
图6.2 信号量的初始函数
下面我们讨论如何获得信号量,它主要包括三个函数,第一个函数表示当信号申请不到时会进程会休眠;对于第二个函数来说,它表示如果当进程因申请不到信号量而进入睡眠后,能被信号打断,这里所说的信号是指进程间通信的信号,比如我们的Ctrl+C,但这时候这个函数的返回值不为0;第三个函数表示信号量无论是否获得,都将立即返回,但返回值会根据是否申请成功而定,同时这个函数也不会导致睡眠。最后的up函数,这个还是很好理解的,就是释放信号量,进而唤醒队列中的等待信号量的进程。函数如图6.3所展示一般。
图6.3 信号量的获得和释放函数
接下来笔者将举几个例子,依次如图6.4,图6.5所示。图6.4的例子仍是实现一个设备只能被一个进程打开。例子很简单,这里便不再细说,主要体现到底如何使用信号量。如图6.4所展示。
图6.4 信号量的使用示例
如图6.5实现的是进程间的同步问题。实际上,当把信号量的初始值为0,则可以实现同步了。正如图6.5所展示一般,对于执行单元A而言,如果执行单元B不执行up函数,执行单元A就因为申请不到进程而睡眠,直至up函数被调用,所以执行代码b前必须等到执行单元B执行完代码c。相信这个内容在操作系统课程都均有提及。
图6.5 信号量实现同步示例
讨论过示例后,相信读者对于信号量的使用有了比较好的了解。下面让我们来简单的讨论下它的实现机制。通过了解信号量的结构体,可以发现结构体中除了使用自旋锁机制外,就是count值的变化(这一点先前已提及)。它的源码如图6.6,图6.7所示。
图6.6 信号量down函数内核源码 图6.7 信号量up函数内核源码
从源码中可以看到信号量利用自旋锁的相关函数实现了对count变量的保护,通过判断变量是否大于0以及自增减来实现信号量的申请和释放。也是因为这一点,所以信号量能够实现同步机制。
另外,关于源码中的__down和__up函数的实现内容,它们其实是维护申请信号量的进程的链表队列(增加、删除操作),以及进程调度方面的一些信息(超时机制),从而让进程实现休眠。由于其中的源码量较为庞大,涉及的内容也较多,故这里不再深入讨论,感兴趣的可以查阅相关资料。
关于信号量的内容还有一点需要提及,我们知道信号量时进程级的,因此对于它的使用必然是当占用资源较长时间的时候。这点在使用信号量的使用需要重点考虑,反之,则容易影响程序的性能等。OK,至此,关于信号量的内容即讨论到此。
七、读写信号量
接下来笔者将讨论有关读写信号量的内容,这部分是较难的一部分,需分析较多的源码。同样,我们首先看下它主要能够做些什么:读写信号量与信号量的关系如同自旋锁与读写自旋锁的关系,它允许N个读操作同时访问共享资源,但最多只能有一个写操作。它的定义位置是在arch\x86\include\asm\rwsem.h以及kernel\rwsem.c中。关于它的结构体的定义,如图7.1所示。显然,它的结构体定义和信号量的结构体定义如出一辙。
图7.1 读写信号量的结构体实现
而后了解下它的具体使用方法,如图7.2所展示的函数。同其它锁机制类似,它所提供的接口函数也是较为简单,包括这些函数能实现的功能都差不了多少。
图7.2 读写信号量的接口函数
在了解读写信号量的定义和使用后,接下来读者将讨论的具体源码实现。它的具体实现是采用汇编实现,比较绕,需配合源码来一一说明,源码次序依次如图7.3至图7.8所示。为便于分析,下面源码只给出最为关键的内容。
对于图7.3所展示的内容主要是读写信号量中需要使用的一些宏定义,具体为何取这个值笔者就不大了解了,后续研究深入了可能会有所体会。
图7.3 读写自旋锁的内核源码
图7.4 读写自旋锁的内核源码
图7.5 读写自旋锁的内核源码
图7.4和同7.5所示源码主要体现了读锁和写锁的实现内容。实际上还是利用test指令检测count变量的值来实现。申请成功的内容好理解,其中的xadd指令表示将原操作数和目的操作数值相交换,而后相加保存入目的操作数。一旦读写信号量申请失败,则需要跳转到如图7.6,图7.7所示的源码中。其中图7.6,图7.7中所示的源码没多少内容,最后还是跳转到rwsem_down_read_failed和rwsem_down_write_failed函数中,通过汇编源码保存改变相应存储变量的寄存器,从而再次返回到C代码中,如图7.8所示的源码中。由于笔者能力有限,对于图7.8所示的源码内容中调用的rwsem_down_write_failed函数被没有研究透,故这里并未进一步深入研究。
至此,关于读写信号量的内容讨论基本结束,确实由于笔者能力有限,关于读写信号量的内容以及本博文的系列内容,读者可进一步深入研究,并欢迎讨论。共同进步。
图7.6 读写自旋锁的内核源码 图7.7 读写自旋锁的内核源码
图7.8 读写自旋锁的内核源码
出于文章篇幅的限制,本篇博文到此结束,后续将会给出《大话Linux内核中锁机制之完成量、互斥量》,感兴趣的读者可继续阅读后一篇博文。由于笔者水平所限,博文中难免有出错之处,欢迎读者指出,大家相互讨论,共同进步。
Linux内核中锁机制之信号量、读写信号量的更多相关文章
- 大话Linux内核中锁机制之信号量、读写信号量
大话Linux内核中锁机制之信号量.读写信号量 在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实 ...
- Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...
- 大话Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
大话Linux内核中锁机制之内存屏障.读写自旋锁及顺序锁 在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论 ...
- Linux内核中锁机制之RCU、大内核锁
在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...
- Linux内核中锁机制之完成量、互斥量
在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...
- Linux内核中锁机制之原子操作、自旋锁
很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其实是由于操作系统中存在多进程对共享资源的并发访问,从而引起了进程间的竞态.这其中包括了我们所熟知的SMP系统,多 ...
- 大话Linux内核中锁机制之RCU、大内核锁
大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核 ...
- 大话Linux内核中锁机制之完成量、互斥量
大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内 ...
- 大话Linux内核中锁机制之原子操作、自旋锁
转至:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html 很多人会问这样的问题,Linux内核中提供了各式各样的同步锁机制到底有何作用?追根到底其 ...
随机推荐
- Insert Interval leetcode java
题目: Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if nec ...
- GoLang中flag标签使用
正如其他语言一样,在 linux 系统上通过传入不同的参数来使得代码执行不同逻辑实现不同功能,这样的优点就是执行想要的既定逻辑而不需要修改代码重新编译与打包.在 Golang 语言中也为我们提供了相应 ...
- MongoDB学习笔记(一)--基础
Insert MongoD ...
- Terminix:基于 GTK3 的平铺式 Linux 终端模拟器
现在,你可以很容易的找到大量的 Linux 终端模拟器,每一个都可以给用户留下深刻的印象.但是,很多时候,我们会很难根据我们的喜好来找到一款心仪的日常使用的终端模拟器.这篇文章中,我们将会推荐一款叫做 ...
- S5PV210开发系列三_简易Bootloader的实现
S5PV210开发系列三 简易Bootloader的实现 象棋小子 1048272975 Bootloader是嵌入式系统上电后第一段运行的代码.对于功能简单的处理器,可能并没有Bo ...
- STS 控制台 中文乱码(maven 中文乱码)
用uriEncoding标签设置中文字符集就行了 <plugin> <groupId>org.apache.tomcat.maven</groupId> <a ...
- Tomcat启动时选择加载项目
到tomcat\conf\Catalina\localhost下新建文件:myapp.xml 内容如下: <Context path="/myapp" docBase=&qu ...
- mysql-cluster 7.3.5-linux 安装
[集群环境] 管理节点 10.0.0.19 数据节点 10.0.0.12 10.0.0.17 sql节点 10.0.0.18 10.0.0 ...
- 【Linux】别名
别名就是一种快捷方式,以省去用户输入一长串命令的麻烦. 别名有多种实现方式,可以使用函数,也可以使用alias命令 注意:alias命令的作用只是短暂的.一旦终端关闭,别名则失效,如果要让别名永久生效 ...
- php-fpm 日志
1.php-fpm 错误日志 #默认位置 安装目录下的 log/php-fpm.log error_log = log/php-fpm.log #错误级别 alert(必须立即处理), error(错 ...