添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
简短概述Linux并发控制与原子操作

简短概述Linux并发控制与原子操作

Linux并发控制

访问共享资源的代码区域称为临界区(Critical Sections)
解决编译乱序问题, 需要通过barrier() 编译屏障进行

#define barrier() __asm__ __volatile__("": : :"memory")

ARM处理器的屏障指令包括:
DMB(数据内存屏障) : 在DMB之后的显式内存访问执行前, 保证所有在DMB指令之前的内存访问完成;

DSB(数据同步屏障) : 等待所有在DSB指令之前的指令完成(位于此指令前的所有显式内存访问均完成, 位于此指令前的所有缓存、 跳转预测和TLB维护操作全部完成) ;
ISB(指令同步屏障) : Flush流水线, 使得所有ISB之后执行的指令都是从缓存或内存中获得的。

读写屏障mb() 、 读屏障rmb() 、 写屏障wmb()

作用于寄存器读写的__iormb() 、 __iowmb()

【文章福利 】小编推荐自己的Linux内核源码交流群:【 869634926 】整理了一些个人觉得比较好的学习书籍、视频资料共享在群里面,有需要的可以自行添加哦!!!前50名可进群领取!!!并额外赠送一份价值600的内核资料包(含视频教程、电子书、实战项目及代码)!!!

学习直通车: Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

中断屏蔽

在单CPU范围内避免竞态的一种简单而有效的方法是在进入临界区之前屏蔽系统的中断。

中断屏蔽将使得中断与进程之间的并发不再发生

local_irq_disable() /* 屏蔽中断 */
. . .
critical section /* 临界区*/
. . .
local_irq_enable() /* 开中断*/

其底层的实现原理是让CPU本身不响应中断, 比如, 对于ARM处理器而言, 其底层的实现是屏蔽ARM CPSR的 I 位

static inline void arch_local_irq_disable(void)
asm volatile(
" cpsid i @ arch_local_irq_disable"
"memory", "cc");

原子操作

原子操作:就是在执行某一操作时不被打断。

linux原子操作问题来源于中断、进程的抢占以及多核smp系统中程序的并发执行。

对于临界区的操作可以加锁来保证原子性,对于全局变量或静态变量操作则需要依赖于硬件平台的原子变量操作。

因此原子操作有两类:一类是各种临界区的锁,一类是操作原子变量的函数。

对于arm来说,单条汇编指令都是原子的,多核smp也是,因为有总线仲裁所以cpu可以单独占用总线直到指令结束,多核系统中的原子操作通常使用内存栅障(memory barrier)来实现,即一个CPU核在执行原子操作时,其他CPU核必须停止对内存操作或者不对指定的内存进行操作,这样才能避免数据竞争问题。但是对于load update store这个过程可能被中断、抢占,所以arm指令集有增加了ldrex/strex这样的实现load update store的原子指令。

自旋锁

自旋锁(Spin Lock) 是一种典型的对临界资源进行互斥访问的手段
为了获得一个自旋锁, 在某CPU上运行的代码需先执行一个原子操作, 该操作测试并设置(Test-AndSet) 某个内存变量。 由于它是原子操作, 所以在该操作完成之前其他执行单元不可能访问这个内存变量。 如果测试结果表明锁已经空闲, 则程序获得这个自旋锁并继续执行; 如果测试结果表明锁仍被占用,程序将在一个小的循环内重复这个“测试并设置”操作, 即进行所谓的“自旋”, 通俗地说就是“在原地打转”
自旋锁主要针对SMP或单CPU但内核可抢占的情况, 对于单CPU和内核不支持抢占的系统, 自旋锁退化为空操作。
在多核SMP的情况下, 任何一个核拿到了自旋锁, 该核上的抢占调度也暂时禁止了, 但是没有禁止另外一个核的抢占调度
尽管用了自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰, 但是得到锁的代码路径在执行临界区的时候, 还可能受到中断和底半部(BH, 稍后的章节会介绍) 的影响。 为了防止这种影响,就需要用到自旋锁的衍生。 spin_lock() /spin_unlock() 是自旋锁机制的基础, 它们和关中断local_irq_disable() /开中断local_irq_enable() 、 关底半部local_bh_disable() /开底半部local_bh_enable() 、 关中断并保存状态字local_irq_save() /开中断并恢复状态字local_irq_restore() 结合就形成了整套自旋锁机制, 关系如下:

spin_lock_irq() = spin_lock() + local_irq_disable()