如何使用linux内核中的内存屏障

时间:2013-06-07 11:43:58

标签: linux memory linux-kernel memory-barriers smp

内核源代码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.我真的很困惑。

2 个答案:

答案 0 :(得分:7)

从标题为“关于记忆屏障可能不会被假设的内容?”的文件部分开始:

  

无法保证在a之前指定的任何内存访问       通过完成内存屏障,内存屏障将完成       指令;屏障可以被认为是在那个CPU中画一条线       访问相应类型的访问队列可能不会交叉。

  

无法保证CPU能够看到正确的效果顺序      来自第二个CPU的访问,即使,如果第二个CPU使用内存      屏障,除非第一个CPU 使用匹配的内存屏障(参见      关于“SMP障碍配对”的小节。)

什么样的内存障碍(当然,以非常简单的方式)确保编译器和CPU内硬件都不会在屏障上重新排序加载(或存储)操作,并且CPU正确感知更改系统其他部分的内存。当负载(或存储)带有其他含义时,这是必要的,例如在访问锁定之前锁定锁定。在这种情况下,让编译器/ CPU通过重新排序来提高访问效率对于我们程序的正确操作是有害的。

阅读本文时,我们需要牢记两件事:

  1. 负载意味着将值从内存(或缓存)传输到CPU寄存器。
  2. 除非CPU共享缓存(或者根本没有缓存),否则它们的缓存系统可能会暂时同步。
  3. 事实#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在同一个高速缓存行上抓住它。