自旋锁spin_lock和raw_spin_lock
自旋锁spin_lock和raw_spin_lock
Linux内核spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析
http://blog.csdn.net/droidphone/article/details/7395983
本文不打算详细探究spin_lock的详细实现机制,只是最近对raw_spin_lock的出现比较困扰,搞不清楚什么时候用spin_lock,什么时候用raw_spin_lock,因此有了这篇文章。
/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
1. 临界区(Critical Section)
能小的范围内。临界区内需要对这些数据对象和硬件对象的访问进行保护,保证在退出临界区前不会被临界区外的代码对这些对象进行修改。出现以下几种情形时,
我们需要使用临界区进行保护:
- (1) 在可以抢占(preemption)的系统中,两个线程同时访问同一个对象;
- (2) 线程和中断同时访问同一个对象;
- (3) 在多核系统中(SMP),可能两个CPU可能同时访问同一个对象;
2. 自旋锁(spin_lock)
- preempt_disable();
- .....
- // 访问共享对象的代码
- ......
- preempt_enable();
同样地,针对单处理器系统,第二种情况,只要临时关闭中断即可,我们可以使用以下方法:
- local_irq_disable();
- ......
- // 访问共享对象的代码
- ......
- local_irq_enable();
那么,针对多处理器的系统,以上的方法还成立么?答案很显然:不成立。
对于第一种情况,虽然抢占被禁止了,可是另一个CPU上还有线程在运行,如果这个线程也正好要访问该共享对象,上面的代码段显然是无能为力了。
对于第二种情况,虽然本地CPU的中断被禁止了,可是另一个CPU依然可以产生中断,如果他的中断服务程序也正好要访问该共享对象,上面的代码段也一样无法对共享对象进行保护。
- spin_lock(spinlock_t *lock);
- spin_unlock(spinlock_t *lock);
对于单处理器系统,在不打开调试选项时,spinlock_t实际上是一个空结构,把上面两个函数展开后,实际上就只是调用
preempt_disable()和preempt_enable(),对于单处理器系统,关掉抢占后,其它线程不能被调度运行,所以并不需要做额外的
工作,除非中断的到来,不过内核提供了另外的变种函数来处理中断的问题。
用preempt_disable()和preempt_enable()防止线程被抢占外,还必须对spinlock_t上锁,这样,如果另一个CPU
的代码要使用该临界区对象,就必须进行自旋等待。
- spin_lock_irq(spinlock_t *lock);
- spin_unlock_irq(spinlock_t *lock);
和:
- spin_lock_irqsave(lock, flags);
- spin_lock_irqrestore(lock, flags);
我们可以按以下原则使用上面的三对变种函数(宏):
- 如果只是在普通线程之间同时访问共享对象,使用spin_lock()/spin_unlock();
- 如果是在中断和普通线程之间同时访问共享对象,并且确信退出临界区后要打开中断,使用spin_lock_irq()/spin_unlock_irq();
- 如果是在中断和普通线程之间同时访问共享对象,并且退出临界区后要保持中断的状态,使用spin_lock_irqsave()/spin_unlock_irqrestore();
其实变种还不止这几个,还有read_lock_xxx/write_lock_xxx、spin_lock_bh/spin_unlock_bh、spin_trylock_xxx等等。但常用的就上面几种。
3. raw_spin_lock
spinlock_t变为了raw_spinlock_t。而且在内核的主线版本中,spin_lock系列只是简单地调用了raw_spin_lock
系列的函数,但内核的代码却是有的地方使用spin_lock,有的地方使用raw_spin_lock。是不是很奇怪?要解答这个问题,我们要回到
2004年,MontaVista Software, Inc的开发人员在邮件列表中提出来一个Real-Time
Linux Kernel的模型,旨在提升Linux的实时性,之后Ingo
Molnar很快在他的一个项目中实现了这个模型,并最终产生了一个Real-Time preemption的patch。
作。当时内核中已经有大约10000处使用了自旋锁的代码,直接修改spin_lock将会导致这个patch过于庞大,于是,他们决定只修改哪些真正不
允许抢占和休眠的地方,而这些地方只有100多处,这些地方改为使用raw_spin_lock,但是,因为原来的内核中已经有
raw_spin_lock这一名字空间,用于代表体系相关的原子操作的实现,于是linus本人建议:
- 把原来的raw_spin_lock改为arch_spin_lock;
- 把原来的spin_lock改为raw_spin_lock;
- 实现一个新的spin_lock;
写到这里不知大家明白了没?对于2.6.33和之后的版本,我的理解是:
- 尽可能使用spin_lock;
- 绝对不允许被抢占和休眠的地方,使用raw_spin_lock,否则使用spin_lock;
- 如果你的临界区足够小,使用raw_spin_lock;
对于没有打上Linux-RT(实时Linux)的patch的系统,spin_lock只是简单地调用raw_spin_lock,实际上他们是完全一
样的,如果打上这个patch之后,spin_lock会使用信号量完成临界区的保护工作,带来的好处是同一个CPU可以有多个临界区同时工作,而原有的
体系因为禁止抢占的原因,一旦进入临界区,其他临界区就无法运行,新的体系在允许使用同一个临界区的其他进程进行休眠等待,而不是强占着CPU进行自旋操
作。写这篇文章的时候,内核的版本已经是3.3了,主线版本还没有合并Linux-RT的内容,说不定哪天就会合并进来,也为了你的代码可以兼容
Linux-RT,最好坚持上面三个原则。
自旋锁spin_lock和raw_spin_lock的更多相关文章
- 自旋锁spin_lock和raw_spin_lock【转】
转自:http://blog.csdn.net/droidphone/article/details/7395983 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 临界区Cr ...
- [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析
转自:http://blog.csdn.net/wh_19910525/article/details/11536279 自旋锁的初衷:在短期间内进行轻量级的锁定.一个被争用的自旋锁使得请求它的线程在 ...
- [内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析【转】
转自:https://www.cnblogs.com/x_wukong/p/8573602.html 转自;https://www.cnblogs.com/aaronLinux/p/5890924.h ...
- 自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave
自旋锁和互斥锁的区别是,自旋锁不会引起睡眠,所以可用于不能休眠的代码中(如IRQ) 自旋锁保持期间抢占失效,而信号量保持期间可以被抢占 定义 spinlock_t lock; init #define ...
- 漫画|Linux 并发、竞态、互斥锁、自旋锁、信号量都是什么鬼?(转)
知乎链接:https://zhuanlan.zhihu.com/p/57354304 1. 锁的由来? 学习linux的时候,肯定会遇到各种和锁相关的知识,有时候自己学好了一点,感觉半桶水的自己已经可 ...
- 自旋锁spinlock
1 在单处理器上的实现 单核系统上,不存在严格的并发,因此对资源的共享主要是多个任务分时运行造成的. 只要在某一时段,停止任务切换,并且关中断(对于用户态应用程序,不大可能与中断处理程序抢临界区资源) ...
- LINUX内核笔记:自旋锁
目录 自旋锁作用与基本使用方法? 在SMP和UP上的不同表现? 自旋锁与上下文 使用spin_lock()后为什么不能睡眠? 强调:锁什么? 参考 1.自旋锁作用与基本使用方法? 与其他锁一样,自 ...
- spinlock自旋锁de使用
Linux内核中最常见的锁是自旋锁.一个自旋锁就是一个互斥设备,它只能有两个值:"锁定"和"解锁".如果锁可用,则"锁定"位被设置,而代码继 ...
- linux内核--自旋锁的理解
http://blog.chinaunix.net/uid-20543672-id-3252604.html 自旋锁:如果内核配置为SMP系统,自旋锁就按SMP系统上的要求来实现真正的自旋等待,但是对 ...
随机推荐
- [大牛翻译系列]Hadoop(20)附录A.10 压缩格式LZOP编译安装配置
附录A.10 LZOP LZOP是一种压缩解码器,在MapReduce中可以支持可分块的压缩.第5章中有一节介绍了如何应用LZOP.在这一节中,将介绍如何编译LZOP,在集群做相应配置. A.10.1 ...
- 常用ASCII 码对照表
目前计算机中用得最广泛的字符集及其编码,是由美国国家标准局(ANSI)制定的ASCII码(American Standard Code for Information Interchange,美国标准 ...
- eclipse插件 代码提示和着色
插件一:Eclipse HTML Editor,用于编辑一些页面文件,例如JSP,HTML,JS等 Eclipse HTML编辑器插件主要提供以下功能:- HTML/JSP/XML/CSS/DTD/J ...
- 批量修改文件后缀(Python)
近期下载了很多各种教程, 但是不幸的是后缀名都是 ".mp4", 而本人喜欢 ".rmvb" 后缀,由于有轻微洁癖, 受不了后面的 ".mp4&quo ...
- Hive内表和外表的区别
本文以例子的形式介绍一下Hive内表和外表的区别.例子共有4个:不带分区的内表.带分区的内表.不带分区的外表.带分区的外表. 1 不带分区的内表 #创建表 create table innerTabl ...
- c# 空接合(??)运算符的运用
相信很多人都看到??运算符,但是不一定每个人都知道它是用来做什么的,之前我也看到过很多次,但是因为一直没有去用过,所以也没有了解他的作用,今天又看到了,所以查了的MSDN,原来??运算符叫做空接合运算 ...
- gtest功能测试一
一.前言 这篇文章主要总结gtest中的所有断言相关的宏. gtest中,断言的宏可以理解为分为两类,一类是ASSERT系列,一类是EXPECT系列.一个直观的解释就是: 1. ASSERT_* 系列 ...
- iOS网络编程同步GET方法请求编程
iOS SDK为HTTP请求提供了同步和异步请求两种不同的API,而且可以使用GET或POST等请求方法.我们先了解其中最为简单的同步GET方法请求. 首先实现查询业务,查询业务请求可以在主视图控制器 ...
- FineUI Grid控件右键菜单的实现
FineUI官方Demo上一直没有Grid右键菜单的实现,其实从4.1.x的版本开始,允许添加自定义的事件监听(Listeners),所以要实现这个功能已经相当容易了. ExtJs右键菜单有很多种,对 ...
- BAT CMD 批处理文件脚本 -1
http://www.cnblogs.com/linglizeng/archive/2010/01/29/Bat-CMD-ChineseVerion.html 1. 综述 ...