futex-based pthread_cond
pthread_cond的实现使用了几个futex来协同进行同步,以及如何来实现的。
假定你已经明白 futex,futex-requeue,以及 pthread lowlevellock。
《linux 内核的futex - requeue 以及 requeue-pi》

pthread_cond一共使用了4个futex,其中包括1个外部的futex,是它所从属的Mutex。
pthread_cond自身包含的3个futex:
__lock:是一个lowlevellock用于pthread_cond原语操作在用户空间的临界区保护(或同步),同时保护pthread_cond的其它变量。
__futex:这是我们所熟知的条件变量阻塞队列。
__nwaiters:用作变量监视器,pthread_cond在destroy使用来同步所有没结束的wait。
这里可以看到futex除了锁之外另一种用法,用户空间可以阻塞在__nwaiters变量上,以Observer角色等待修改的一方发出更新的信号。pthread_cond_destroy会一直阻塞/唤醒监视__nwaiters,直到__nwaiters等于0为止。
pthread_cond_wait就是先后阻塞在__futex和__mutex.__lock两个futex队列进行等待,也就是futex_requeue系统调用中的futex1与futex2。pthread_cond_broadcast使用futex_requeue将__futex上的阻塞线程requeue到__mutex.__lock的等待队列上。
__total_seq 是 cond_wait调用的次序号。
__nwaiters 是 阻塞中的cond_wait的计数。
__wakeup_seq 是 发起唤醒的次序号,包括cond_signal 以及 cond_wait的timeout。
__broadcast_seq 是 cond_broadcast调用的次序号。
__woken_seq 是 cond_wait从阻塞中被唤醒的次序号。
__futex 是发起wakeup或wait的动作次序号。
规则:
__futex 在 __total_seq 和 __total_seq * 2 之间推进。__futex 为 __total_seq 和 __wakeup_seq 之和。
__total_seq 先于 __wakeup_seq 向前推进。每次wait调用使__total_seq向前推进1。后随的wakeup才能使__wakeup_seq向前推进。
__wakeup_seq 先于 __woken_seq 向前推进。wait被唤醒后将__woken_seq向前推进1,结束一次wait。
__broadcast_seq,独立的调用次序。每次broadcast,都会将__wakeup_seq,__wokenup_seq,以及__futex平衡,根据 __total_seq。
__nwaiters 未结束的 wait 调用,destroy 必须监视这个值,同步到这个值等于0才能安全进行销毁。
只有当__total_seq 不等于 __wakeup_seq 时,才能进行 signal 或 broadcast 。
当wait发现__futex这个参考次序号有变动,回到用户空间去调查情况:
1. 发现__wakeup_seq次序号有变动,即表明此时有signal调用,就可以与signal所唤醒的waiter进行竞争,竞争同步在用户空间临界区。如果__wakeup_seq != __woken_seq表明自己可以获得这次通行,如果__wakeup_seq == __woken_seq表示这次通行已经被某个waiter拔得头筹,只能决定再进内核排队。
2. 发现__broadcast_seq次序号有变动,即表明自身正包含在broadcast当中(自己修改的__total_seq,被broadcast采纳),可以不再进入内核排队,而直接获得通行。
wait都是三心两意的,一发现__futex有推进,就先打消排队的意愿,怀着被broadcast选中或幸运抢占到一次signal带来的机会,转头回到用户空间张望一下,没戏才失望地再次进入内核排队,但在排队前还是不死心。
下面来看pthread_cond_wait,pthread_cond_signal,以及pthread_cond_broadcast是如何在用户空间临界区同步的:
红色框:__lock保护的用户空间临界区。
紫色框:futex系统调用。

这里可以看到signal是全过程排他(mutual exclusion)进行的。而wait和broadcast则是用户空间和内核(系统调用)分开进行临界区同步的,它们在用户空间的代码只在用户空间的临界区同步,系统调用可以并发进行,当然也不是完全并发,在内核中系统调用的执行还是会同步进行的。换个说法,系统调用并不阻塞其它线程在用户空间在代码,一些线程阻塞在用户空间的临界区,不影响另一些线程去进行系统调用。
broadcast在唤醒线程时,是不会阻塞其它线程去调用wait的。但是signal 即使进入内核去唤醒线程时,也得阻塞其它线程去调用condvar的函数。
broadcast 与 broadcast 并发:
broadcast与broadcast同时调用,同步在用户空间临界区次序后面的broadcast,因为__total_seq == __wakeup_seq而退出,只有同步在用户临界区的第一个broadcast会继续单独执行。
signal 与 wait 和 broadcast 并发:
当signal调用时,可能有前面的wait或broadcast刚进入内核,但在signal结束之前,不允许任何用户空间的调用进入临界区。这情况下,前面的wait必须回到用户空间重新进入临界区,在signal调用完全结束之后,才能再次进入内核。
a. signal调用在broadcast执行期间,signal会因为__total_seq == __wakeup_seq而退出。
b. broadcast调用在signal执行期间,只能阻塞在用户空间临界区,等待signal调用结束。
c. signal调用时,前面已经有wait在执行,signal会将未能在内核临界区排队的wait全部延后,原理是,wait在内核临界区发现futex有变,即表明用户空间层有变动,必须先回到用户空间,延后次序再次进入内核。
d. wait调用在signal执行期间,wait只能阻塞在用户空间临界区,等待signal调用结束。
broadcast 与 wait 并发:
broadcast与wait同时调用,wait都会回到用户空间,检查自身是否包含在本次broadcast中,同步在用户空间临界区,先于broadcast的wait被包含在broadcast,后面的wait则排除在外。包含在本次broadcast中的wait则可以不用在内核排队而直接获得通行。
其它同步或锁的还有
《linux 内核的spinlock如何实现排队》
《linux 内核的各种futex功能》
《linux 内核的rt_mutex (realtime互斥体)》
《linux 内核的futex pi-support,即pi-futex使用rt_mutex委托》
futex-based pthread_cond的更多相关文章
- linux 内核的futex
futex是linux内核为用户空间实现锁等同步机制而设计的同步排队(队列queueing)服务.在futex.c的注释中,futex起源于"Fast Userspace Mutex&quo ...
- linux 内核的futex - requeue 以及 requeue-pi
futex为更好支持pthread_cond的实现(,最主要是broadcast),设计了requeue功能,并以futex系统调用提供操作接口,包括一对配对的操作 futex_wait_requeu ...
- futex-based pthread_cond 源代码分析
pthread_cond的实现使用了几个futex来协同进行同步,以及如何来实现的. 假定你已经明白 futex,futex-requeue,以及 pthread lowlevellock. < ...
- Distributed MVCC based cross-row transaction
The algorithm for supporting distributed MVCC based cross-row transactions on top of a distributed k ...
- Plant Design Review Based on AnyCAD
Plant Design Review Based on AnyCAD eryar@163.com Abstract. AVEVA Review is used to 3D model visuali ...
- CQRS, Task Based UIs, Event Sourcing agh!
原文地址:CQRS, Task Based UIs, Event Sourcing agh! Many people have been getting confused over what CQRS ...
- Prototype based langue LUA
Prototype-based programming https://en.wikipedia.org/wiki/Prototype-based_programming Prototype-base ...
- 原创: How to build a query based on Definition Updates installed
In SCCM 2012 R2, you can use following class. Use SMS_CombinedDeviceResources.EPAntivirusSignatureLa ...
- Spike Notes on Lock based Concurrency Concepts
Motivation 承并发编程笔记Outline,这篇文章专注于记录学习基于锁的并发概念的过程中出现的一些知识点,为并发高层抽象做必要的准备. 尽管存在Doug Lee开山之作Concurrent ...
随机推荐
- Android学习探索之本地原生渲染 LaTeX数据公式
前言: 一直致力于为公司寻找更加高效的解决方案,作为一款K12在线教育App,功能中难免会有LaTeX数学公式的显示需求,这部分公司已经实现了此功能,只是个人觉得在体验和效率上还是不太好,今天来聊一下 ...
- 关于struts2中的default-action-ref
struts2中的default-action-ref一般用于,在请求无效或错误时将请求指引到错误页面.我这次的用法是在请求首页之前先发送请求到后台,进行数据获取后再转至首页显示,但是出了一个问题,d ...
- 纯Jquery前端分页
---恢复内容开始--- 由于之前自己做过jquery分页,就是调用jni接口时,只能用前台分页解决显示问题.最近看到有人提这样的问题:一个请求传过来上万个数据怎么办?于是萌生了写这篇博客的想法. 效 ...
- windows phone 8.1开发:文件选择器FileSavePicker
上一篇文章小梦分享了文件选择器FileOpenPicker的用法,这篇文章我们继续分享FileSavePicker的用法,FileSavePicker的用法几乎和FileOpenPicker用法一模一 ...
- 获取 metadata 过程详解 - 每天5分钟玩转 OpenStack(167)
接上节,启动 neutron router 后 instance c1 终于拿到了 metadata, 从下面 c1 的启动日志可知: c1 所认为的 metadata 服务地址是 169.254.1 ...
- iOS开发之@Selector参数的传递
比如: NSMutableDictionary *dic = [[NUMutableDictionary alloc]init]; [dic setValue:@”haha” forKey:@”tes ...
- Centos7配置文件共享服务器SAMBA三步曲(转)
1.安装 yum install samba samba-client samba-common -y 2.配置 备份已有配置 mv /etc/samba/smb.conf /etc/samba/sm ...
- 解决codeblock不能运行的问题
codeblock 编译失败 软件 IDE codeblock这软件的确不错,但是除此安装使用就会不小心入坑.你是不是满心欢喜的下载好codeblock,敲入代码,点击运行的时候却总是没反应呢? 如果 ...
- String 类的实现(1)浅拷贝存在的问题
浅拷贝 : 也称位拷贝 , 编译器只是直接将指针的值拷贝过来, 结果多个对象共用 同 一块内 存, 当一个对象将这块内 存释放掉之后, 另 一些对象不知道该块空间 已经还给了 系 统, 以 为还有效, ...
- Vijos1144小胖守皇宫【树形DP】
皇宫看守 太平王世子事件后,陆小凤成了皇上特聘的御前一品侍卫.皇宫以午门为起点,直到后宫嫔妃们的寝宫,呈一棵树的形状:某些宫殿间可以互相望见.大内保卫森严,三步一岗,五步一哨,每个宫殿都要有人全天候看 ...