pthread的lowlevellock
pthread的lowlevellock是futex的最简单的锁应用。也是pthread其它同步原语最基本的锁。lowlevellock提供(或实现)了三种锁(方法),一是基于0或1的互斥的锁规则,二是基于robust futex定义的锁规则,三是用于condition重新对临界区上锁的操作。
lowlevellock使用的是non-pi futex。futex的锁规则由用户空间来定义,这里的用户空间是glibc。
normal lowlevellock 定义的0或1互斥的futex锁规则为:
0,锁无持有者。
1,锁有持有者。
2,锁竞争。
两个锁操作 __lll_lock 和 __lll_unlock。
上锁操作 __lll_lock:
使用atomic_compare_and_exchange尝试对futex上锁,如果这时锁状态为0,完成上锁,futex标记状态为1;否则必须标记futex为锁竞争状态,转而进入锁竞争等待,调用futex系统调用的futex_wait操作进行排队。因为用户空间并不知道内核的futex队列中是否还有其它锁竞争的任务在等待,所以系统调用阻塞唤醒回到用户空间,对futex尝试上锁,必须以锁竞争状态来上锁,以使自己解锁时,会调用futex_wake。
// lll lock fastpath
#define __lll_lock(futex, private) \
((void) \
({ \
int *__futex = (futex); \
if (__glibc_unlikely \
(atomic_compare_and_exchange_bool_acq (__futex, , ))) \
{ \
if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \
__lll_lock_wait_private (__futex); \
else \
__lll_lock_wait (__futex, private); \
} \
}))
// lll lock slowpath
void
__lll_lock_wait_private (int *futex)
{
if (*futex == )
lll_futex_wait (futex, , LLL_PRIVATE); /* Wait if *futex == 2. */ while (atomic_exchange_acq (futex, ) != )
lll_futex_wait (futex, , LLL_PRIVATE); /* Wait if *futex == 2. */
}
解锁操作 __lll_unlock:
函数在用户空间直接用atomic_exchange将futex状态替换为0,如果发现原来状态为锁竞争,则调用futex系统调用的futex_wake操作,从内核的futex_queue中选出等待任务,将其唤醒,使唤醒的任务回到用户空间完成上锁。
// lll unlock
#define __lll_unlock(futex, private) \
((void) \
({ \
int *__futex = (futex); \
int __private = (private); \
int __oldval = atomic_exchange_rel (__futex, ); \
if (__glibc_unlikely (__oldval > )) \
lll_futex_wake (__futex, , __private); \
}))
上锁操作使用atomic_compare_and_exchange函数进行fastpath尝试,确保与其它锁操作不相容(排它)并且保证状态转换是从0到1,修改可能会失败。
上锁操作使用atomic_exchange函数进行slowpath尝试。slowpath的上锁操作只要修改为锁竞争,并不需要确保锁从何种状态下才能转换状态。当状态从0进行转换,表示无锁,但是现在处理slowpath,表示任务是从锁竞争排队中被唤醒的,可能还有其它任务进行锁竞争而排队。当状态从1进行转换,虽然任务被唤醒了,但是锁被另一任务的fastpath执行偷了,仍然是锁竞争。当状态从2进行转换,无可非议锁竞争。所以在slowpath的上锁操作只要修改为锁竞争状态,并且根据原状态来判断锁的状态转换次序,确认自己是否上锁。
解锁操作使用atomic_exchange直接修改锁状态为无锁。并不需要确保状态转换的关系,也不用参照状态转换的前状态,所以解锁是不会失败的。
__lll_lock_wait函数是上锁操作的 slowpath,它包含了一个循环trylock-wait-loop,在循环中不断重复尝试上锁和失败进行内核排队,直到上锁操作成功被接受。而__lll_unlock解锁操作则对锁进行一次绝对的修改,然后决定是否要进入内核唤醒阻塞的任务。为什么阻塞的任务被唤醒,并不意味这个任务得到了锁,只是被通知解锁事件,这个任务必须再次尝试上锁。为什么被唤醒任务不在内核进行上锁操作,确保锁得到后才返回用户空间,因为处于内核的调用系统并不知道上锁的规则。又由于解锁操作和唤醒阻塞队列是分开的,会futxe唤醒的系统调用也不会理解futex的锁规则,只作单纯的阻塞队列唤醒工作。因此在解锁一刻起,有一线程(运行在另一CPU)发起上锁操作,马上就可以完成上锁,而被__lll_unlock唤醒的任务并没有取得锁,待它回到用户空间时,它尝试上锁就会发现锁被使用了,再次进入内核回到阻塞队列。
robust lowlevellock 使用robust futex定义的锁规则:
futex分为三个状态字段,是否上锁,是否锁竞争,是否持锁线程死。
是否上锁,为整型的0-29位,0代表无锁,非0代表上锁,并且上锁状态同时保存持锁线程的id号。
是否持锁线程死,为整型30位。
是否锁竞争,为整型31位。
两个配对锁操作 __lll_robust_lock 和 __lll_robust_unlock。
它与前的非robust规则的lowlevellock的异同。
一,都使用non-pi futex的futex系统调用操作futex_wait和futex_wake进行锁竞争仲裁。执行系统调用的内核并不理会锁规则,同时上锁和解锁操作都在用户空间进行,内核只进行排队和唤醒。
二,锁都是二态互斥。0代表无锁,非0代表上锁。不同的是robust规则用tid代替非0状态,而非robust规则将上锁为1。
三,robust锁(lowlevellock)将锁竞争状态与上锁状态分开两个字段标记,而非robust锁(lowlevellock)将锁竞争状态作为上锁状态的另一形态。
四,robust锁必须标记持锁线程的生存状态,以供robust算法使用。
五,虽然robust lowlevellock使用了robust锁规则,但并没有提供robust处理服务,同时也没有应用futex提供的robust特性。
我们从代码来比较它们的区别:
normal lowlevellcok 和 robust lowlevellock 在 lock-fastpath 的区别:
相同的算法骨架,先使用atomic_compare_and_exchange函数尝试上锁,确保上锁从状态0转换,失败后进入slowpath。
normal lowlevellcok 和 robust lowlevellock 在 lock-slowpath 的区别:
相同的算法骨架,在循环中尝试上锁,上锁令锁进入锁竞争状态,根据转换前的状态确认自己是否上锁,发现上锁失败后使用futex_wait进入内核进行排队阻塞。
不同的是 robust lowlevellock 使用atomic_compare_and_exchange进行尝试,确保持锁线程的tid不被错入。当发现有锁持有线程死了必须返回,由锁的使用层来进行锁的恢复。
normal lowlevellcok 和 robust lowlevellock 在 unlock 的区别:
两者都通过atomic_exchange直接对锁状态进行修改为0,无视锁当前状态。如果发现锁状态修改前存在锁竞争,进入内核唤醒阻塞任务。
这里注意的是,robust锁unlcok和发生owner died,两种情况是不相容的,因为发生owner died表示任务没机会进行unlock,当任务unlock时不可能owner died,自己不是作为owner现在活得好好的进行unlock。
最后就是用于condition重新进行临界区的上锁操作 __lll_cond_lock。
由于condition是从一个临界区中进入一个条件的阻塞,阻塞在条件变量的期间离开临界区,条件变量通知时重新进入临界区。由于重新进入临界区时,不是通知fastpath上锁,而是从wait_requeue系统调用中返回,相当于slowpath,所以这时的上锁操作与slowpath一样。
pthread的lowlevellock的更多相关文章
- futex-based pthread_cond
pthread_cond的实现使用了几个futex来协同进行同步,以及如何来实现的. 假定你已经明白 futex,futex-requeue,以及 pthread lowlevellock. < ...
- linux 内核的futex
futex是linux内核为用户空间实现锁等同步机制而设计的同步排队(队列queueing)服务.在futex.c的注释中,futex起源于"Fast Userspace Mutex&quo ...
- phtread_mutex 组合
phtread_mutex通过mutexattr设定其类型,并保存在成员__kind中.pthread_mutex的锁操作函数根据__kind进行方法的分派(dispatch).__kind由5个字段 ...
- futex-based pthread_cond 源代码分析
pthread_cond的实现使用了几个futex来协同进行同步,以及如何来实现的. 假定你已经明白 futex,futex-requeue,以及 pthread lowlevellock. < ...
- 4.1/4.2 多线程进阶篇<上>(Pthread & NSThread)
本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人 “简书” 本文源码 Demo 详见 Githubhttps://github.com/shorfng ...
- Windows下使用Dev-C++开发基于pthread.h的多线程程序
一.下载Windows版本的pthread 目前最新版本是:pthreads-w32-2-9-1-release.zip. 二.解压pthread到指定目录 我选择的目录是:E:\DEV-C ...
- NPTL vs PThread
NPTL vs PThread POSIX threads (pthread) is not an implementation, it is a API specification (a stand ...
- Linux pthread
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h& ...
- VS2013 配置pthread
参考:http://blog.csdn.net/qianchenglenger/article/details/16907821 一.下载地址 ftp://sourceware.org/pub/pth ...
随机推荐
- Java基础一:面向对象的特征
经过16年校招,自己在Java基础方面也算有了一个质的飞跃,从原来知其然,到现在知其所以然,现将学习心得总结于此. 首先需要知道类和对象是什么? 维基百科定义类为: an extensible pro ...
- 实现类似MVC ViewBag类型的对象
public class ViewBag : DynamicObject { private readonly Dictionary<string,dynamic> dic=new Dic ...
- 1.WP8.1开发_去除闪动效果,直接进入首页
有时候希望打开软件的时候,不出现闪屏,而直接进入到第一个页面. 把第一个页面作为闪屏,可以制作一些进度条 和动画等... 很简单,有两种方法去除闪屏. 1.删除项目下 Assets 文件夹里面的Sp ...
- 用SSE指令计算点乘和累加
void sse_mul_float:两段内存float数据点乘,结果覆盖第一组内存. float sse_acc_float:一组内存float值累加. 注: 1. 没有考虑中间的精确问题,结果会有 ...
- iOS开发之延时执行
方法1: performSelector(SEL) withObjects:(id) afterDelay:(NSTimeInterval); 方法2: dispatch_after(dispatch ...
- react+webpack开发环境配置
react是目前非常热门的前端框架,提倡组件化开发.所谓的组件,简单理解,就是一个独立的页面部件(包括页面模版,样式,逻辑等),它是一个独立的整体. webpack,是一个模块打包工具,其主要功能,就 ...
- iOS 文本转语音(TTS)详解:Swift
上一篇博客讲解了iOS的speech FrameWork语音识别的功能:http://www.cnblogs.com/qian-gu-ling/p/6599670.html,对应的这篇博客就写一下文本 ...
- Git 入门和常用命令详解
git 使用使用教程 git 使用简易指南 常用 Git 命令清单 下载 https://git-scm.com/downloads 工作流 本地仓库由三部分组成. 工作区:保存实际的文件( ...
- 原创SQlServer数据库生成简单的说明文档小工具(附源码)
这是一款简单的数据库文档生成工具,主要实现了SQlServer生成说明文档的小工具,目前不够完善,主要可以把数据库的表以及表的详细字段信息,导出到 Word中,可以方便开发人员了解数据库的信息或写技术 ...
- 20155231 2016-2017-2 《Java程序设计》第5周学习总结
# 20155231 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 学习目标 理解异常架构 掌握try...catch...finally处理异常的方法 会 ...