Skip to content

Latest commit

 

History

History
85 lines (49 loc) · 5.61 KB

003-原子性操作.md

File metadata and controls

85 lines (49 loc) · 5.61 KB

#原子操作 ##内存屏障 没有依赖关系的内存操作实际会以随机的顺序执行,但对CPU-CPU的交互和I/O来说却是个问题。我们需要某种方式来指导编译器和CPU以约束执行顺序。内存屏障就是这样一种干预手段。它们会给屏障两侧的内存操作强加一个偏序关系。

这种强制措施是很重要的,因为一个系统中,CPU和其它硬件可以使用各种技巧来提高性能,包括内存操作的重排、延迟和合并;预取;推测执行分支以及各种类型的缓存。内存屏障是用来禁用或抑制这些技巧的,使代码稳健地控制多个CPU和(或)设备的交互。

##内存屏障分类 ###写入屏障(store) 写入屏障保证: 所有该屏障之前的store操作,一定在所有该屏障之后的store操作之前执行。

写入屏障仅保证store指令上的偏序关系,不要求对load指令有什么影响。

随着时间推移,可以视CPU提交了一系列store操作到内存系统。在该一系列store操作中,写入屏障之前的所有store操作将在该屏障后面的store操作之前执行。

###读出屏障(load) 读出屏障保证: 所有该屏障之前的load操作,看起来一定在所有该屏障之后的load操作之前执行。

读出屏障仅保证load指令上的偏序关系,不要求对store指令有什么影响。

读出屏障包含了数据依赖屏障的功能,因此可以替代数据依赖屏障。

###数据依赖屏障

数据依赖屏障是读出屏障的一种较弱形式。在执行两个load指令,第二个依赖于第一个的执行结果(例如:第一个load执行获取某个地址,第二个load指令取该地址的值时,可能就需要一个数据依赖屏障,来确保第二个load指令在获取目标地址值的时候,第一个load指令已经更新过该地址)。

数据依赖屏障仅保证相互依赖的load指令上的偏序关系,不要求对store指令,无关联的load指令以及重叠的load指令有什么影响。

如写入屏障中提到的,可以视系统中的其它CPU提交了一些列store指令到内存系统,然后the CPU being considered就能感知到。由该CPU发出的数据依赖屏障可以确保任何在该屏障之前的load指令,如果该load指令的目标被另一个CPU的存储(store)指令修改,在屏障执行完成之后,所有在该load指令对应的store指令之前的store指令的更新都会被所有在数据依赖屏障之后的load指令感知。

###通用内存屏障

通用屏障确保所有该屏障之前的load和store操作,看起来一定在所有屏障之后的load和store操作之前执行。

通用屏障能保证load和store指令上的偏序关系。

通用屏障包含了读出屏障和写入屏障,因此可以替代它们两者。

##内存屏障需要注意的事情

###不能保证的事情 任何在内存屏障之前的内存访问操作能在内存屏障指令执行完成时也执行完成;内存屏障相当于在CPU的访问队列中划了一条界线,相应类型的指令不能跨过该界线。

一个CPU发出的内存屏障能对另一个CPU或该系统中的其它硬件有任何直接影响。只会间接影响到第二个CPU看第一个CPU的存取操作发生的顺序,但请看下一条:

一个CPU看到第二个CPU存取操作的结果的顺序,即使第二个CPU使用了内存屏障,除非第一个CPU也使用与第二个CPU相匹配的内存屏障。

一些CPU相关的硬件不会对内存访问重排序。 CPU缓存的一致性机制会在多个CPU之间传播内存屏障的间接影响,但可能不是有序的。

###SMP屏障配对

当处理CPU-CPU之间的交互时,相应类型的内存屏障总应该是成对出现的。缺少相应的配对屏障几乎可以肯定是错误的。

写入屏障应始终与数据依赖屏障或者读出屏障配对,虽然通用内存屏障也是可以的。同样地,读出屏障或数据依赖屏障应至少始终与写入屏障配对使用,虽然通用屏障仍然也是可以的。

##原子操作函数结尾 ###mb结尾函数

完整的内存屏障,以原子操作为界,之前的所有加载和写入操作,以及之后的所有加载和写入操作,都不能跨越原子操作的界线。

###relb结尾函数 以原子操作为界,之前的所有加载和写入操作都不能跨越原子操作的界线。

如果原子操作是写入操作,那么可以在写入的原子操作前面放置一个通用内存屏障和写入屏障的组合。如果原子操作是加载操作,那么可以在加载的原子操作之前放置一个读出内存屏障和通用内存屏障的组合。

###acqb结尾函数 以原子操作为界,之后的所有加载和写入操作都不能跨越原子操作的界线。

如果原子操作是加载操作,那么可以在加载的原子操作之后放置一个通用内存屏障和读出屏障的组合。如果原子操作是写操作,那么可以在写入的原子操作之后放置一个通用内存屏障和写入内存屏障的组合。

###wb结尾函数 以原子操作为界,之前的所有写入操作,以及之后的所有写入操作,都不能跨越原子操作的界线。

###rb结尾函数 以原子操作为界,之前的所有加载操作,以及之后的所有加载操作,都不能跨越原子操作的界线。

###ddrb结尾函数 表示只要确保有依赖关系的加载操作。这个屏障的实现取决于具体的编译器和硬件平台。

###原子操作和锁

锁释放操作通常是占有锁的线程写入一个变量表示锁已经释放了。获得锁的时候通常都是读取一个表示是否锁定的共享变量。