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)

在没有干预的情况下,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 = &B 时生效,这意味着 B 将等于 2。对于 CPU 2,B 在获得值时应该是 2 C,也就是&B,为什么它会认为B是7。我真的很困惑。

4

2 回答 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

例如,推测性的“加载 B”可能发生,因为 B 与早期感兴趣的其他一些变量或硬件预取抓取它位于同一缓存行上。

于 2013-06-07T22:11:42.947 回答
8

从标题为“内存屏障不能假设什么?”的文件部分:

无法保证在内存屏障之前指定的任何内存访问都将通过内存屏障指令的完成而完成;可以认为屏障在该 CPU 的访问队列中画了一条线,适当类型的访问可能不会交叉。

即使第二个 CPU 使用内存屏障,也不能保证 CPU 会从第二个 CPU 的访问中看到正确的效果顺序,除非第一个 CPU使用匹配的内存屏障(请参阅“SMP 屏障配对”小节)。

内存屏障所做的(当然是以一种非常简化的方式)是确保编译器和 CPU 内的硬件都不会执行任何巧妙的尝试来重新排序跨屏障的加载(或存储)操作,并且 CPU 正确地感知到系统其他部分产生的内存。当加载(或存储)具有其他含义时,这是必要的,例如在访问我们正在锁定的任何内容之前锁定一个锁。在这种情况下,让编译器/CPU 通过对它们重新排序来提高访问效率,这对我们程序的正确操作是有害的。

阅读本文档时,我们需要牢记两点:

  1. 加载意味着将值从内存(或缓存)传输到 CPU 寄存器。
  2. 除非 CPU 共享缓存(或根本没有缓存),否则它们的缓存系统可能会暂时与我们同步。

事实 #2 是一个 CPU 对数据的感知与另一个不同的原因之一。虽然缓存系统旨在在一般情况下提供良好的性能和一致性,但在特定情况下可能需要一些帮助,例如文档中说明的情况。

一般来说,正如文档所建议的那样,涉及多个 CPU 的系统中的屏障应该配对,以强制系统同步两个(或所有参与的)CPU 的感知。想象这样一种情况,一个 CPU 完成了加载或存储,并且主内存已更新,但新数据尚未传输到第二个 CPU 的缓存,导致两个 CPU 之间缺乏一致性。

我希望这有帮助。我建议再次阅读 memory-barriers.txt 时牢记这一点,尤其是标题为“CPU 缓存的影响”的部分。

于 2013-06-07T13:10:49.450 回答