内核源代码Documentation / memory-barriers.txt中有一个例子,如下所示:
CPU 1 CPU 2 ======================= ======================= { B = 7; X = 9; Y = 8; C = &Y } STORE A = 1 STORE B = 2 <write barrier> STORE C = &B LOAD X STORE D = 4 LOAD C (gets &B) LOAD *C (reads B)
如果没有干预,CPU 2可能会在某些情况下感知CPU 1上的事件 尽管CPU 1发出了写屏障,但是有效的随机顺序:
+-------+ : : : : | | +------+ +-------+ | Sequence of update | |------>| B=2 |----- --->| Y->8 | | of perception on | | : +------+ \ +-------+ | CPU 2 | CPU 1 | : | A=1 | \ --->| C->&Y | V | | +------+ | +-------+ | | wwwwwwwwwwwwwwww | : : | | +------+ | : : | | : | C=&B |--- | : : +-------+ | | : +------+ \ | +-------+ | | | |------>| D=4 | ----------->| C->&B |------>| | | | +------+ | +-------+ | | +-------+ : : | : : | | | : : | | | : : | CPU 2 | | +-------+ | | Apparently incorrect ---> | | B->7 |------>| | perception of B (!) | +-------+ | | | : : | | | +-------+ | | The load of X holds ---> \ | X->9 |------>| | up the maintenance \ +-------+ | | of coherence of B ----->| B->2 | +-------+ +-------+ : :
我不明白,因为我们有写屏障,所以,任何商店必须在C =&amp; B执行时生效,这意味着B将等于2.对于CPU 2,B应该是2时它得到C的值,即&amp; B,为什么它会将B视为7.我真的很困惑。
答案 0 :(得分:7)
从标题为“关于记忆屏障可能不会被假设的内容?”的文件部分开始:
无法保证在a之前指定的任何内存访问 通过完成内存屏障,内存屏障将完成 指令;屏障可以被认为是在那个CPU中画一条线 访问相应类型的访问队列可能不会交叉。
和
无法保证CPU能够看到正确的效果顺序 来自第二个CPU的访问,即使,如果第二个CPU使用内存 屏障,除非第一个CPU 也使用匹配的内存屏障(参见 关于“SMP障碍配对”的小节。)
什么样的内存障碍(当然,以非常简单的方式)确保编译器和CPU内硬件都不会在屏障上重新排序加载(或存储)操作,并且CPU正确感知更改系统其他部分的内存。当负载(或存储)带有其他含义时,这是必要的,例如在访问锁定之前锁定锁定。在这种情况下,让编译器/ CPU通过重新排序来提高访问效率对于我们程序的正确操作是有害的。
阅读本文时,我们需要牢记两件事:
事实#2是一个CPU能够以不同方式感知数据的原因之一。虽然缓存系统旨在提供一般情况下的良好性能和一致性,但在特定情况下可能需要一些帮助,如文档中所示。
通常,正如文档所暗示的那样,涉及多个CPU的系统中的障碍应该配对,以迫使系统同步两个(或所有参与的)CPU的感知。想象一个CPU完成加载或存储并更新主存储器的情况,但新数据尚未传输到第二个CPU的缓存,导致两个CPU之间缺乏连贯性。
我希望这会有所帮助。我建议再次阅读memory-barriers.txt,特别是题为“CPU缓存的影响”一节。
答案 1 :(得分:7)
关键缺失点是对序列的错误假设:
LOAD C (gets &B)
LOAD *C (reads B)
第一次加载必须在第二次加载之前。弱有序的架构可以“好像”发生以下情况:
LOAD B (reads B)
LOAD C (reads &B)
if( C!=&B )
LOAD *C
else
Congratulate self on having already loaded *C
例如,推测性的“LOAD B”可能发生,因为B与早期感兴趣的其他变量或硬件预取的B在同一个高速缓存行上抓住它。