大话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内核中锁机制之完成量、互斥量》,感兴趣的读者可继续阅读后一篇博文。由于笔者水平所限,博文中难免有出错之处,欢迎读者指出,大家相互讨论,共同进步。

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

大话Linux内核中锁机制之信号量、读写信号量的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 说说JSON和JSONP区别

    前言 由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现. 当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的Socke ...

  2. canvas toDataURL() 方法如何生成部分画布内容的图片

    HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI .可以使用 type参数其类型,默认为 PNG 格式.图片的分辨率为96dpi. 如果画布的高 ...

  3. gulp快速将css中的px替换成rem

    1.Gulp安装配置 1.全局安装gulp 1.1 安装 命令提示符执行cnpm install gulp -g; 1.2 查看是否正确安装:命令提示符执行gulp -v,出现版本号即为正确安装. 2 ...

  4. AngularJS+RequireJs实现动态加载JS和页面的方案研究【上】

    1.入口页面 存放地址:src/main/webapp/WEB-INF/view/workflow/workflow.jsp [html] view plain copy 在CODE上查看代码片派生到 ...

  5. number to string

    C++进行int to string和string to int 下面方法一存在内存泄露 #include<strstream>void main(){ std::strstream ss ...

  6. 微信小程序-03-小程序开发框架

    微信小程序-03-小程序开发框架 官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/MINA.html 小程序开发框架 ...

  7. 使用Virtual Audio Cable软件实现电脑混音支持电脑录音

    http://blog.csdn.net/cuoban/article/details/50552644

  8. [翻译] GSProgressView

    GSProgressView 本人极不推荐使用drawRect的方式来绘制下载进度条,无论机器的性能怎么高,使用drawRect用于绘制图形都是低效的. A cute little circular ...

  9. yum 安装 lnmp

    一. 系统 更新 yum -y update 二. 安装nginx 创建文件 vim /etc/yum.repos.d/nginx.repo 文件内容,这配置是安装最新的稳定版1.8 [nginx] ...

  10. PHP调优

    目录 php.ini 内存 Zend OPcache 文件上传 最长执行时间 处理会话 缓冲输出 真实路径缓存 php.ini PHP解释器在 php.ini 文件中配置和调优.web和cli使用的路 ...