1.并发与竞态
并发(Concurrency)是指多个单元同时、并行被执行,而并发执行单元对共享资源(硬件资源和软件上的全局变量,静态变量等)的访问很容易导致竞态(Race Conditions)。
竞争状态的分类:
对称多处理器(SMP)的多个CPU | SMP是一种紧耦合、共享存储的系统类型,因为多个CPU同时共享系统总线,因此可以访问共同的外设和存储器 |
单CPU内进程与抢占它的进程 | linux2.6以后支持内核抢占调度,一个进程在内核执行的时候可能耗完了自己的时间片,也可能被另一个高优先级进程打断,进程和抢占它的进程访问共享资源,竞态发生 |
中断(硬中断、软中断、Tasklet、底半部)与进程之间 | 中断打断进程,中断打断中断(中断程序访问进程或另一个中断正在访问的资源,则竞态发生) |
竞态的解决方法是:
保证对共享资源的互斥访问(即一个执行单元在访问共享资源时,其他的执行单元被禁止访问)访问共享资源的代码区域被称为临界区(critical sections),临界区需要被以某种互斥机制加以保护。
Linux常见互斥机制:中断屏蔽、原子操作、自旋锁和信号量、互斥体等
2.编译乱序和执行乱序
程序在运行时内存实际的访问顺序和程序代码编写的访问顺序不一定一致,这就是内存乱序访问。内存乱序访问行为出现的理由是为了提升程序运行时的性能。内存乱序访问主要发生在两个阶段:
- 编译时,编译器优化导致内存乱序访问(指令重排)
- 运行时,多 CPU 间交互引起内存乱序访问
防止编译乱序:
未加屏障 | 加编译屏障 |
// test.cpp int x, y, r; void f() { x = r; y = 1; } |
int x, y, r; void f() { x = r; __asm__ __volatile__("" ::: "memory"); y = 1; } |
|
编译后,对于x的内存访问必定在y赋值之前 |
执行乱序:(主要表现在多CPU上)
如果是单核CPU,执行程序时碰到依赖点(如f=1;while(f==0);//会等待f=1执行完,再执行while),会等待,因此程序员感受不到乱序;
但是,这个依赖点等待对于其他核是不可见的,例如:
CPU0:
while(f==0);//wait
printf(x);
CPU1:
x=42;
f=1;