当前位置: 首页 > news >正文

自旋锁spin_lock和raw_spin_lock

本文不打算详细探究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依然可以产生中断,如果他的中断服务程序也正好要访问该共享对象,上面的代码段也一样无法对共享对象进行保护。

实际上,在linux中,上面所说的三种情况都可以用自旋锁(spin_lock)解决。基本的自旋锁函数对是:
spin_lock(spinlock_t *lock);
spin_unlock(spinlock_t *lock);
对于单处理器系统,在不打开调试选项时,spinlock_t实际上是一个空结构,把上面两个函数展开后,实际上就只是调用preempt_disable()和preempt_enable(),对于单处理器系统,关掉抢占后,其它线程不能被调度运行,所以并不需要做额外的工作,除非中断的到来,不过内核提供了另外的变种函数来处理中断的问题。

对于多处理器系统,spinlock_t实际上等效于内存单元中的一个整数,内核保证spin_lock系列函数对该整数进行原子操作,除了调用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
在2.6.33之后的版本,内核加入了raw_spin_lock系列,使用方法和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,最好坚持上面三个原则。
---------------------
作者:droidphone
来源:CSDN
原文:https://blog.csdn.net/droidphone/article/details/7395983
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://www.cnblogs.com/Ph-one/p/10980318.html

相关文章:

  • git 优雅的撤销中间某次提交
  • 让个人域名下GithubPage完美支持https
  • CloudFlare 新手入门中文教程
  • DNS解析类型的区别
  • 免费版CloudFlare CDN基本设置参考
  • Git missing Change-Id in commit message footer解决方法
  • 循环神经网络(RNN)模型与前向反向传播算法
  • 在博客中使用MathJax写数学公式
  • 强化学习(一)模型基础[转]
  • 强化学习(二)马尔科夫决策过程(MDP)
  • Android 格式化分区命令
  • Keil综合(03)_map文件全解析[转]
  • MSP与PSP
  • 从Cortex-M3的MSP 和PSP谈Linux能否在中断中使用Sleep
  • 地球是圆的怎么还分东西方
  • (三)从jvm层面了解线程的启动和停止
  • CentOS从零开始部署Nodejs项目
  • CSS选择器——伪元素选择器之处理父元素高度及外边距溢出
  • Debian下无root权限使用Python访问Oracle
  • FastReport在线报表设计器工作原理
  • IDEA 插件开发入门教程
  • Javascript 原型链
  • log4j2输出到kafka
  • text-decoration与color属性
  • 从伪并行的 Python 多线程说起
  • 大快搜索数据爬虫技术实例安装教学篇
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 实现菜单下拉伸展折叠效果demo
  • ​渐进式Web应用PWA的未来
  • # 透过事物看本质的能力怎么培养?
  • #ubuntu# #git# repository git config --global --add safe.directory
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (11)MSP430F5529 定时器B
  • (2020)Java后端开发----(面试题和笔试题)
  • (27)4.8 习题课
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET MVC之AOP
  • .NET Standard 的管理策略
  • .net 受管制代码
  • .net解析传过来的xml_DOM4J解析XML文件
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网
  • [Asp.net mvc]国际化
  • [AX]AX2012 R2 出差申请和支出报告
  • [C++进阶篇]STL中vector的使用
  • [FxCop.设计规则]8. 也许参数类型应该是基类型
  • [git] windows系统安装git教程和配置
  • [Head First设计模式]策略模式
  • [ICCV2017]Neural Person Search Machines
  • [JS真好玩] 掘金创作者必备: 监控每天是谁取关了你?