memory barrier && fence

内存屏障,可以保证在此之前的代码全部执行完才开始执行在此之后的代码

参考wikipedia的定义:
Memory barrier, also known as membar or memory fence or fence instruction, is a type of barrier and a class of instruction which causes a central processing unit (CPU) or compiler to enforce an ordering constraint on memory operations issued before and after the barrier instruction.

 

http://en.wikipedia.org/wiki/Memory_barrier

一个例子:
Processor #1:
x = f = 0
loop:
load the value in location f, if it is 0 goto loop
print the value in location x

Processor #2:
store the value 42 into location x
store the value 1 into location f

上面的例子中,变量 x f 初始化值都是0。我们期望输出“42”。但是结果并不总是这样。
如果 Processor #2 的执行顺序是乱序的(out-of-order execution),也就是说,对f赋值的语句先于对x赋值的语句,那么就有可能输出“0”
对于大多数的程序来说,这种特例情况是不能容忍的。
如果将内存屏障置于对f赋值的语句之前,那么就能保证 Processor #2 先对x赋值,然后才对f赋值。这样就能得到我们期望的结果,输出“42”

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------

本文摘录自:

http://lxr.linux.no/linux+v2.6.27.8/Documentation/memory-barriers.txt 

抽象内存访问模型

CPU在执行程序的时候会产生很多内存访问操作。然而,CPU在访问内存操作的顺序是很随意的,它以任意自己喜欢的方式来执行程序提供的访存指令。除此之外,编译器同样会对访存指令按照它认定的方式进行修改。

例(1)

假定CPU 1和CPU 2 执行一下操作:

    CPU 1           CPU 2

 =============== ===============

{ A == 1; B == 2 }

  A = 3;          x = A;

  B = 4;          y = B;

对于这样的一些内存操作,可能出现的顺序有:

1)   STORE A=3,      STORE B=4,      x=LOAD A->3,    y=LOAD B->4
2)   STORE A=3,      STORE B=4,      y=LOAD B->4,    x=LOAD A->3
3)   STORE A=3,      x=LOAD A->3,    STORE B=4,      y=LOAD B->4
4)   STORE A=3,      x=LOAD A->3,    y=LOAD B->2,    STORE B=4
5)   STORE A=3,      y=LOAD B->2,    STORE B=4,      x=LOAD A->3
6)   STORE A=3,      y=LOAD B->2,    x=LOAD A->3,    STORE B=4
7)   STORE B=4,      STORE A=3,      x=LOAD A->3,    y=LOAD B->4

可能出现的结果有:

x == 1, y == 2
x == 1, y == 4
x == 3, y == 2
x == 3, y == 4

例(2)

       CPU 1           CPU 2
=============== ===============
{ A == 1, B == 2, C = 3, P == &A, Q == &C }
B = 4;          Q = P;
P = &B          D = *Q;

在这个操作序列中,有明显的数据依赖关系。存储在D中的值依赖于CPU 2通过P取得的地址。可能的结果有:

 (Q == &A) and (D == 1)
(Q == &B) and (D == 2)
(Q == &B) and (D == 4) 

设备操作

对于有些设备,它们的寄存器映射在内存中。当访问某些控制寄存器来进行设备的控制的时候,访问顺序就相当关键。比如,访问一个以太网卡的内部寄存器时,必须通过一个地址端口寄存器A和数据端口寄存器D来进行,如果要访问内部寄存器5,可能会使用如下代码:

*A = 5

 x = *D

但实际执行的序列可能是:

STORE *A = 5, x = LOAD *D
x = LOAD *D, STORE *A = 5

这里的第二个执行序列肯定会导致错误,因为它在访问地址端口寄存器A前访问了数据端口寄存器D。

 

CPU能够保证的顺序

1)  对任何给定的CPU,相互依赖的内存访问将会按照顺序执行,比如:

Q = P; D = *Q;

CPU执行的顺序始终是:

Q = LOAD P, D = LOAD *Q

2)  相重叠的加载、存储操作将会按照顺序执行,比如:

a = *X; *X = b;

CPU执行的顺序始终是:

a = LOAD *X, STORE *X = b 
    比如:
    *X = c; d = *X;
    CPU执行的顺序是:
STORE *X = c, d = LOAD *X 
不能被假定保证顺序
1)不能假定独立的加载、存储将会按照顺序执行,比如:
X = *A; Y = *B; *D = Z;

         其中可能出现的操作序列有:

X = LOAD *A,  Y = LOAD *B,  STORE *D = Z
X = LOAD *A,  STORE *D = Z, Y = LOAD *B
Y = LOAD *B,  X = LOAD *A,  STORE *D = Z
Y = LOAD *B,  STORE *D = Z, X = LOAD *A
STORE *D = Z, X = LOAD *A,  Y = LOAD *B
STORE *D = Z, Y = LOAD *B,  X = LOAD *A

2)  有些重叠的内存访问可能合并或者丢弃,比如:

X = *A; Y = *(A + 4);

可能的执行顺序有:

X = LOAD *A; Y = LOAD *(A + 4);

Y = LOAD *(A + 4); X = LOAD *A;

{X, Y} = LOAD {*A, *(A + 4) };

        

         比如:

    *A = X; Y = *A;

         可能执行的顺序有:

STORE *A = X; Y = LOAD *A;
STORE *A = Y = X;

根据以上分析,对于独立的内存操作以随机的方式进行,它是相当的有效的,但与这也给CPU之间的交互和IO操作来说带来了问题。这个时候必须使用Memory Barrier来保证各种操作按照顺序执行。

posted @ 2012-08-15 09:12  hljyunxi  阅读(572)  评论(0编辑  收藏  举报