- Concurrency and Race Conditions-Why introduce?
在Linux内核早期结构中,跟并发相关的资源有限,并未将SMP( Symmetric
multiprocessing system)考虑进来。而在当时唯一能够产生并发的是硬件中断服务,内核对于此种场景一种极为简单的方式(进入临界区域后关闭掉中断,退出后重新打开),但此种方式不再适用于当今多核多线程多中断的场景,因此内核也得与时俱进,到今天提供了一系列跟并发有关系的API接口,可以应用在不同的平台上。
LDD3中关于 Concurrency and Race Conditions的背景交代的很清楚,大概就是说当前多内核,多线程,且单内核支持抢占和中断的现代环境下,程序运行过程中不可避免的会发生资源的并发访问并从而产生竞赛,如果内核作为整个系统的“大管家”不加以治理和疏通,情况将是灾难性的,继续贴图来说明这种“race”有多激烈:
在多核多线程多中断的场景下,并发并不局限的存在于单个内核中,内核间也有并发的存在,总结下来:
单核
进程时间片耗完,轮到其他进程执行;
进程被高优先级进程抢占;
进程被中断抢占;
中断被高优先级中断抢占;
SMP多核CPU
核间进程之间;
核间进程与中断之间;
核间中断与中断
在这样一种残酷的竞争下,内核的措施和方案也有条不紊的推出,虽然这些关于并发控制的方案形态各异,适用于不同的场景,但其核心思想从未改变过,这里引用一下经典:making sure that only one thread of execution can manipulate a shared resource at any time.
2.Concurrency and Race Conditions:中断屏蔽,Semaphores,Mutexes,spin_lock and completion
中断屏蔽:
Linux内核当中提供了local_irq_disable和local_irq_enable来实现中断屏蔽,具体而言,进入到临界区域后所在核内的中断与进程之间的并发不会发生,因为Linux内核的进程调度都依赖于中断来实现。
但是,由于由于Linux内核的进程调度,异步IO等重要的操作都依赖于中断,在中断屏蔽期间所有的中断都无法得到处理,因此长时间使用中断屏蔽是很危险的,有可能造成数据丢失乃至系统崩溃的结果。另外local_irq_disable和local_irq_enable只能屏蔽本cpu上的中断响应,对于SMP多cpu引发的竞态无能为力,因此在驱动程序中单独使用local_irq_disable和local_irq_enable通常意味着一个bug。
内核中还提供了local_irq_save和local_irq_restore,其原理除了禁止中断响应外,还保存目前cpu的中断位信息,临界区结束后恢复中断信息。看得出来, local_irq_save和local_irq_restore相较于local_irq_disable和local_irq_enable更具备“人情味儿”,可以在临界区域逗留更长时间,因此前者的组合跟自旋锁(spin_lock)搭配形成的API在内核中很常见。
原子操作:
spin_lock:
Semaphores: