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

dsp*_*pjm 7 linux memory smp linux-kernel memory-barriers

内核源代码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)
Run Code Online (Sandbox Code Playgroud)

在没有干预的情况下,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  |       +-------+
                                            +-------+
                                            :       :
Run Code Online (Sandbox Code Playgroud)

我不明白,因为我们有写屏障,所以,任何商店必须在执行C =&B时生效,这意味着B将等于2.对于CPU 2,当B得到值时,B应为2 C,这是&B,为什么它会将B视为7.我真的很困惑.

Arc*_*son 8

关键缺失点是对序列的错误假设:

LOAD C (gets &B)
LOAD *C (reads B)
Run Code Online (Sandbox Code Playgroud)

第一次加载必须在第二次加载之前.弱有序的架构可以"好像"发生以下情况:

LOAD B (reads B)  
LOAD C (reads &B)
if( C!=&B ) 
    LOAD *C
else
    Congratulate self on having already loaded *C
Run Code Online (Sandbox Code Playgroud)

例如,推测性的"LOAD B"可能发生,因为B与早期感兴趣的其他变量或硬件预取的B在同一个高速缓存行上.


ido*_*oby 7

从标题为"关于记忆障碍可能不被假设的文件?"的部分开头:

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

有没有保证,一个CPU会看到的效果,从第二个CPU的访问正确的顺序,甚至如果第二CPU采用的是记忆障碍,除非第一CPU 采用了匹配内存屏障(见"SMP障碍配对"小节).

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

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

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

事实#2是一个CPU能够以不同方式感知数据的原因之一.虽然缓存系统旨在提供一般情况下的良好性能和一致性,但在特定情况下可能需要一些帮助,如文档中所示.

通常,如文档所示,涉及多个CPU的系统中的障碍应该配对,以迫使系统同步两个(或所有参与的)CPU的感知.想象一种情况,其中一个CPU完成加载或存储并更新主存储器,但新数据尚未传输到第二个CPU的缓存,导致两个CPU之间缺乏连贯性.

我希望这有帮助.我建议再次阅读memory-barriers.txt,特别是题为"CPU缓存的影响"一节.