Linux内核中的内存屏障是如何使用的?

7
在内核源码文档“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)

Without intervention, CPU 2 may perceive the events on CPU 1 in some effectively random order, despite the write barrier issued by 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 =&B时都必须生效,这意味着此时B应该等于2。对于CPU 2,当它获取C(即&B)的值时,B应该已经是2了,为什么它会认为B是7?我真的很困惑。

2个回答

8
关键的遗漏点是错误地假设了以下序列:
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与前面某个感兴趣的变量在同一缓存行上或者硬件预取抓取了它。


1
现实中很少有真正的机制来重新排序依赖负载。硬件(或编译器)需要一些理由来猜测从&B加载是一种可能有用的方式,以满足一个地址尚未知晓的负载。值预测是一种方法;一些DEC Alpha模型具有分段L1d高速缓存,可以产生这种效果。分支预测是另一种方式。所以,它确实会发生,但机制比简单的硬件预取要奇怪得多。 - Peter Cordes

8
从标题为“内存屏障不保证什么?”的部分可以得知,没有任何保证在内存屏障之前指定的任何内存访问都会在内存屏障执行完成后“完全”结束;可以认为内存屏障是在CPU的访问队列中画了一条线,以使适当类型的访问不会越过该线。此外,即使第二个CPU使用内存屏障,也不能保证第一个CPU能够正确地看到第二个CPU访问的效果(参见“SMP屏障配对”一节),除非第一个CPU也使用匹配的内存屏障。内存屏障可以在非常简化的情况下确保编译器和CPU硬件都不会尝试在屏障上重新排序加载(或存储)操作,并且CPU能够正确地感知系统其他部分对内存所做的更改。当加载(或存储)承载额外的含义时,如在访问我们要锁定的内容之前锁定锁定,这是必需的。在这种情况下,让编译器/CPU通过重排序来使访问更有效率是对我们程序正确运行的一种危险。阅读本文件时需要记住以下两点:1.加载意味着将值从内存(或高速缓存)传输到CPU寄存器;2.除非CPU共享缓存(或根本没有缓存),否则它们的缓存系统可能会瞬间不同步。这是CPU可能会以不同方式感知数据的原因之一。虽然缓存系统设计用于在一般情况下提供良好的性能和一致性,但在像文档中所示的特定情况下可能需要一些帮助。通常情况下,应该将多个CPU涉及的屏障配对,以强制系统同步两个(或所有参与)CPU的感知。可以想象一种情况,其中一个CPU完成加载或存储,并且主内存已更新,但新数据尚未传输到第二个CPU的缓存中,导致两个CPU之间缺乏一致性。建议你再次阅读文档,特别是题为“CPU缓存的影响”的部分,希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接