设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Linux内核的内存屏障

2013-7-3 14:02| 发布者: 红黑魂| 查看: 2849| 评论: 0|来自: ifeve

摘要: 原文链接作者:David Howells、Paul E. McKenney译者:曹姚君校对:丁一内容:抽象的内存访问模型设备操作保障什么是内存屏障?内存屏障的种类什么是内存屏障不能确保的?数据依赖屏障控制依赖SMP屏障配对内存屏障顺 ...

原文链接 作者:David HowellsPaul E. McKenney 译者:曹姚君 校对:丁一

内容:

  1. 抽象的内存访问模型
  2. 什么是内存屏障?
  3. 显式内核屏障
  4. 隐式内核内存屏障
  5. CPU之间的锁屏障效应
  6. 什么地方需要内存障碍?
  7. 内核的I/O屏障效应
  8. 假想的最小执行顺序模型
  9. CPU缓存的影响
  10. CPU能做到的
  11. 使用示例
  12. 引用

抽象的内存访问模型

考虑下面这个系统的抽象模型:

		            :                :
		            :                :
		            :                :
		+-------+   :   +--------+   :   +-------+
		|       |   :   |        |   :   |       |
		|       |   :   |        |   :   |       |
		| CPU 1 |<----->| Memory |<----->| CPU 2 |
		|       |   :   |        |   :   |       |
		|       |   :   |        |   :   |       |
		+-------+   :   +--------+   :   +-------+
		    ^       :       ^        :       ^
		    |       :       |        :       |
		    |       :       |        :       |
		    |       :       v        :       |
		    |       :   +--------+   :       |
		    |       :   |        |   :       |
		    |       :   |        |   :       |
		    +---------->| Device |<----------+
		            :   |        |   :
		            :   |        |   :
		            :   +--------+   :
		            :                :

每个CPU执行一个有内存访问操作的程序。在这个抽象的CPU中,内存操作的顺序是非常宽松的。假若能让程序的因果关系看起来是保持着的,CPU就可以以任意它喜欢的顺序执行内存操作。同样,只要不影响程序的结果,编译器可以以它喜欢的任何顺序安排指令。

因此,上图中,一个CPU执行内存操作的结果能被系统的其它部分感知到,因为这些操作穿过了CPU与系统其它部分之间的接口(虚线)。

例如,请考虑以下的事件序列:

	CPU 1		CPU 2
	===============	===============
	{ A == 1; B == 2 }
	A = 3;		x = A;
	B = 4;		y = B;

内存系统能看见的访问顺序可能有24种不同的组合:

	STORE A=3,	STORE B=4,	x=LOAD A->3,	y=LOAD B->4
	STORE A=3,	STORE B=4,	y=LOAD B->4,	x=LOAD A->3
	STORE A=3,	x=LOAD A->3,	STORE B=4,	y=LOAD B->4
	STORE A=3,	x=LOAD A->3,	y=LOAD B->2,	STORE B=4
	STORE A=3,	y=LOAD B->2,	STORE B=4,	x=LOAD A->3
	STORE A=3,	y=LOAD B->2,	x=LOAD A->3,	STORE B=4
	STORE B=4,	STORE A=3,	x=LOAD A->3,	y=LOAD B->4
	STORE B=4, ...
	...

因此,可能产生四种不同的值组合:

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

此外,一个CPU 提交store指令到存储系统,另一个CPU执行load指令时感知到的这些store的顺序可能并不是第一个CPU提交的顺序。

另一个例子,考虑下面的事件序列:

          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)

注意:CPU 2永远不会将C的值赋给D,因为CPU在对*Q发出load指令之前会先将P赋给Q。

硬件操作

一些硬件的控制接口,是一组存储单元,但这些控制寄存器的访问顺序是非常重要的。例如,考虑拥有一系列内部寄存器的以太网卡,它通过一个地址端口寄存器(A)和一个数据端口寄存器(D)访问。现在要读取编号为5的内部寄存器,可能要使用下列代码:

	*A = 5;
	x = *D;

但上面代码可能表现出下列两种顺序:

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

其中第二个几乎肯定会导致故障,因为它在读取寄存器之后才设置地址值。

保障

下面是CPU必须要保证的最小集合:

  • 任意CPU,有依赖的内存访问指令必须按顺序发出。这意味着对于
    			Q = P; D = *Q;
    		

    CPU会发出下列内存操作:

    			Q = LOAD P, D = LOAD *Q
    		

    并且总是以这种顺序。

  • 在一个特定的CPU中,重叠的load和store指令在该CPU中将会看起来是有序的。这意味着对于:
    		a = *X; *X = b;
    		

    CPU发出的内存操只会是下面的顺序:

    		a = LOAD *X, STORE *X = b
    		

    对于:

    		*X = c; d = *X;
    		

    CPU只会发出:

    		STORE *X = c, d = LOAD *X
    		

    (如果load和store指令的目标内存块有重叠,则称load和store重叠了。)。

还有一些必须要一定不能假设的东西:

  • 一定不能假设无关联的load和store指令会按给定的顺序发出,这意味着对于:
    	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
    
  • 必须要假定重叠的内存访问可能会被合并或丢弃。这意味着对于
    	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-CPU的交互和I / O来说却是个问题。我们需要某种方式来指导编译器和CPU以约束执行顺序。

内存屏障就是这样一种干预手段。它们会给屏障两侧的内存操作强加一个偏序关系。

这种强制措施是很重要的,因为一个系统中,CPU和其它硬件可以使用各种技巧来提高性能,包括内存操作的重排、延迟和合并;预取;推测执行分支以及各种类型的缓存。内存屏障是用来禁用或抑制这些技巧的,使代码稳健地控制多个CPU和(或)设备的交互。

内存屏障的种类

内存屏障有四种基本类型:

  1. write(或store)内存屏障。
  2. write内存屏障保证:所有该屏障之前的store操作,看起来一定在所有该屏障之后的store操作之前执行。

    write屏障仅保证store指令上的偏序关系,不要求对load指令有什么影响。

    随着时间推移,可以视CPU提交了一系列store操作到内存系统。在该一系列store操作中,write屏障之前的所有store操作将在该屏障后面的store操作之前执行。

    [!]注意,write屏障一般与read屏障或数据依赖障碍成对出现;请参阅“SMP屏障配对”小节。

  3. 数据依赖屏障。

    数据依赖屏障是read屏障的一种较弱形式。在执行两个load指令,第二个依赖于第一个的执行结果(例如:第一个load执行获取某个地址,第二个load指令取该地址的值)时,可能就需要一个数据依赖屏障,来确保第二个load指令在获取目标地址值的时候,第一个load指令已经更新过该地址。

    数据依赖屏障仅保证相互依赖的load指令上的偏序关系,不要求对store指令,无关联的load指令以及重叠的load指令有什么影响。

    write(或store)内存屏障中提到的,可以视系统中的其它CPU提交了一些列store指令到内存系统,然后the CPU being considered就能感知到。由该CPU发出的数据依赖屏障可以确保任何在该屏障之前的load指令,如果该load指令的目标被另一个CPU的存储(store)指令修改,在屏障执行完成之后,所有在该load指令对应的store指令之前的store指令的更新都会被所有在数据依赖屏障之后的load指令感知。

    参考”内存屏障顺序实例”小节图中的顺序约束。

    [!]注意:第一个load指令确实必须有一个数据依赖,而不是控制依赖。如果第二个load指令的目标地址依赖于第一个load,但是这个依赖是通过一个条件语句,而不是实际加载的地址本身,那么它是一个控制依赖,需要一个完整的read屏障或更强的屏障。查看”控制依赖”小节,了解更多信息。

    [!]注意:数据依赖屏障一般与写障碍成对出现;看到“SMP屏障配对”章节。

  4. read(或load)内存屏障。

    read屏障是数据依赖屏障外加一个保证,保证所有该屏障之前的load操作,看起来一定在所有该屏障之后的load操作之前执行。

    read屏障仅保证load指令上的偏序关系,不要求对store指令有什么影响。

    read屏障包含了数据依赖屏障的功能,因此可以替代数据依赖屏障。

    [!]注意:read屏障通常与write屏障成对出现;请参阅“SMP屏障配对”小节。

  5. 通用内存屏障。

    通用屏障确保所有该屏障之前的load和store操作,看起来一定在所有屏障之后的load和store操作之前执行。

    通用屏障能保证load和store指令上的偏序关系。

    通用屏障包含了read屏障和write屏障,因此可以替代它们两者。

一对隐式的屏障变种:

  1. LOCK操作。

    LOCK操作可以看作是一个单向渗透的屏障。它保证所有在LOCK之后的内存操作看起来一定在LOCK操作后才发生。

    LOCK操作之前的内存操作可能会在LOCK完成之后发生。

    LOCK操作几乎总是与UNLOCK操作成对出现。

  2. UNLOCK操作。

    这也是一个单向渗透屏障。它保证所有UNLOCK操作之前的内存操作看起来一定在UNLOCK操作之前发生。

    UNLOCK操作之后的内存操作可能会在UNLOCK完成之前发生。

    LOCK和UNLOCK操作严格保证自己对指令的顺序。

    使用了LOCK和UNLOCK操作,一般就不需要其它类型的内存屏障了(但要注意在”MMIO write屏障”一节中提到的例外情况)。

仅当两个CPU之间或者CPU与其它设备之间有交互时才需要屏障。如果可以确保某段代码中不会有任何这种交互,那么这段代码就不需要内存屏障。

注意,这些是最低限度的保证。不同的架构可能会提供更多的保证,但是它们不是必须的,不应该依赖其写代码(they may not be relied upon outside of arch specific code)。

什么是内存屏障不能确保的?

有一些事情,Linux内核的内存屏障并不保证:

  • 不能保证,任何在内存屏障之前的内存访问操作能在内存屏障指令执行完成时也执行完成;内存屏障相当于在CPU的访问队列中划了一条界线,相应类型的指令不能跨过该界线。
  • 不能保证,一个CPU发出的内存屏障能对另一个CPU或该系统中的其它硬件有任何直接影响。只会间接影响到第二个CPU看第一个CPU的存取操作发生的顺序,但请看下一条:
  • 不能保证,一个CPU看到第二个CPU存取操作的结果的顺序,即使第二个CPU使用了内存屏障,除非第一个CPU也使用与第二个CPU相匹配的内存屏障(见”SMP屏障配对”小节)。
  • 不能保证,一些CPU相关的硬件[*]不会对内存访问重排序。 CPU缓存的一致性机制会在多个CPU之间传播内存屏障的间接影响,但可能不是有序的。

    [*]总线主控DMA和一致性相关信息,请参阅:

    Documentation/PCI/pci.txt

    Documentation/PCI/PCI-DMA-mapping.txt

    Documentation/DMA-API.txt

数据依赖屏障

数据依赖屏障的使用条件有点微妙,且并不总是很明显。为了阐明问题,考虑下面的事件序列:

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

这里很明显存在数据依赖,看起来在执行结束后,Q不是&A就是&B,并且:

	(Q == &A) 意味着 (D == 1)
	(Q == &B) 意味着 (D == 4)

但是,从CPU 2可能先感知到P更新,然后才感知到B更新,这就导致了以下情况:

	(Q == &B) and (D == 2) ????

虽然这可能看起来像是一致性或因果关系维护失败,但实际并不是的,且这种行为在一些真实的CPU上也可以观察到(如DEC Alpha)。

为了处理这个问题,需要在地址load和数据load之间插入一个数据依赖屏障或一个更强的屏障:

	CPU 1		CPU 2
	===============	===============
	{ A == 1, B == 2, C = 3, P == &A, Q == &C }
	B = 4;
	<write barrier>
	P = &B
			Q = P;
			<data dependency barrier>
			D = *Q;

这将迫使结果为前两种情况之一,而防止了第三种可能性的出现。

[!]注意:这种极其有违直觉的场景,在有多个独立缓存(split caches)的机器上很容易出现,比如:一个cache bank处理偶数编号的缓存行,另外一个cache bank处理奇数编号的缓存行。指针P可能存储在奇数编号的缓存行,变量B可能存储在偶数编号的缓存行中。然后,如果在读取CPU缓存的时候,偶数的bank非常繁忙,而奇数bank处于闲置状态,就会出现指针P(&B)是新值,但变量B(2)是旧值的情况。

另外一个需要数据依赖屏障的例子是从内存中读取一个数字,然后用来计算某个数组的下标;

	CPU 1		CPU 2
	===============	===============
	{ M[0] == 1, M[1] == 2, M[3] = 3, P == 0, Q == 3 }
	M[1] = 4;
	<write barrier>
	P = 1
			Q = P;
			<data dependency barrier>
			D = M[Q];

数据依赖屏障对RCU系统是很重要的,如,看include/linux/ rcupdate.h的rcu_dereference()函数。这个函数允许RCU的指针被替换为一个新的值,而这个新的值还没有完全的初始化。

更多详细的例子参见”高速缓存一致性”小节。

控制依赖

控制依赖需要一个完整的read内存屏障来保证其正确性,而不简单地只是数据依赖屏障。考虑下面的代码:

	q = &a;
	if (p)
		q = &b;
	<data dependency barrier>
	x = *q;

这不会产生想要的结果,因为这里没有实际的数据依赖,而是一个控制依赖,CPU可能会提前预测结果而使if语句短路。在这样的情况下,实际需要的是下面的代码:

	q = &a;
	if (p)
		q = &b;
	<read barrier>
	x = *q;

SMP屏障配对

当处理CPU-CPU之间的交互时,相应类型的内存屏障总应该是成对出现的。缺少相应的配对屏障几乎可以肯定是错误的。

write屏障应始终与数据依赖屏障或者read屏障配对,虽然通用内存屏障也是可以的。同样地,read屏障或数据依赖屏障应至少始终与write屏障配对使用,虽然通用屏障仍然也是可以的:

	CPU 1		CPU 2
	===============	===============
	a = 1;
	<write barrier>
	b = 2;		x = b;
			<read barrier>
			y = a;

或者:

	CPU 1		CPU 2
	===============	===============================
	a = 1;
	<write barrier>
	b = &a;		x = b;
			<data dependency barrier>
			y = *x;

基本上,那个位置的read屏障是必不可少的,尽管可以是“更弱“的类型。

[!]注意:write屏障之前的store指令通常与read屏障或数据依赖屏障后的load指令相匹配,反之亦然:

	CPU 1                           CPU 2
	===============                 ===============
	a = 1;           }----   --->{  v = c
	b = 2;           }    \ /    {  w = d
	<write barrier>        \        <read barrier>
	c = 3;           }    / \    {  x = a;
	d = 4;           }----   --->{  y = b;

内存屏障序列实例

首先,write屏障确保store操作的偏序关系。考虑以下事件序列:

	CPU 1
	=======================
	STORE A = 1
	STORE B = 2
	STORE C = 3
	<write barrier>
	STORE D = 4
	STORE E = 5

这一连串的事件提交给内存一致性系统的顺序,可以使系统其它部分感知到无序集合{ STORE A,STORE B, STORE C } 中的操作都发生在无序集合{ STORE D, STORE E}中的操作之前:

	+-------+       :      :
	|       |       +------+
	|       |------>| C=3  |     }     /\
	|       |  :    +------+     }-----  \  -----> Events perceptible to
	|       |  :    | A=1  |     }        \/       the rest of the system
	|       |  :    +------+     }
	| CPU 1 |  :    | B=2  |     }
	|       |       +------+     }
	|       |   wwwwwwwwwwwwwwww }   <--- At this point the write barrier
	|       |       +------+     }        requires all stores prior to the
	|       |  :    | E=5  |     }        barrier to be committed before
	|       |  :    +------+     }        further stores may take place
	|       |------>| D=4  |     }
	|       |       +------+
	+-------+       :      :
	                   |
	                   | Sequence in which stores are committed to the
	                   | memory system by CPU 1
	                   V

其次,数据依赖屏障确保于有数据依赖关系的load指令间的偏序关系。考虑以下事件序列:

	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 1发出了write屏障,CPU2感知到的CPU1上事件的顺序也可能是随机的:

	+-------+       :      :                :       :
	|       |       +------+                +-------+  | 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  |       +-------+
	                                        +-------+
	                                        :       :

在上述的例子中,尽管load *C(可能是B)在load C之后,但CPU 2感知到的B却是7;

然而,在CPU2中,如果数据依赖屏障放置在loadC和load *C(即:B)之间:

	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)
				<data dependency barrier>
				LOAD *C (reads B)

将发生以下情况:

	+-------+       :      :                :       :
	|       |       +------+                +-------+
	|       |------>| B=2  |-----       --->| Y->8  |
	|       |  :    +------+     \          +-------+
	| CPU 1 |  :    | A=1  |      \     --->| C->&Y |
	|       |       +------+       |        +-------+
	|       |   wwwwwwwwwwwwwwww   |        :       :
	|       |       +------+       |        :       :
	|       |  :    | C=&B |---    |        :       :       +-------+
	|       |  :    +------+   \   |        +-------+       |       |
	|       |------>| D=4  |    ----------->| C->&B |------>|       |
	|       |       +------+       |        +-------+       |       |
	+-------+       :      :       |        :       :       |       |
	                               |        :       :       |       |
	                               |        :       :       | CPU 2 |
	                               |        +-------+       |       |
	                               |        | X->9  |------>|       |
	                               |        +-------+       |       |
	  Makes sure all effects --->   \   ddddddddddddddddd   |       |
	  prior to the store of C        \      +-------+       |       |
	  are perceptible to              ----->| B->2  |------>|       |
	  subsequent loads                      +-------+       |       |
	                                        :       :       +-------+

第三,read屏障确保load指令上的偏序关系。考虑以下的事件序列:

	CPU 1			CPU 2
	=======================	=======================
		{ A = 0, B = 9 }
	STORE A=1
	<write barrier>
	STORE B=2
				LOAD B
				LOAD A

在没有其它干涉时,尽管CPU1发出了一个write屏障,CPU 2感知到的CPU 1中事件的顺序也可能是随机的:

	+-------+       :      :                :       :
	|       |       +------+                +-------+
	|       |------>| A=1  |------      --->| A->0  |
	|       |       +------+      \         +-------+
	| CPU 1 |   wwwwwwwwwwwwwwww   \    --->| B->9  |
	|       |       +------+        |       +-------+
	|       |------>| B=2  |---     |       :       :
	|       |       +------+   \    |       :       :       +-------+
	+-------+       :      :    \   |       +-------+       |       |
	                             ---------->| B->2  |------>|       |
	                                |       +-------+       | CPU 2 |
	                                |       | A->0  |------>|       |
	                                |       +-------+       |       |
	                                |       :       :       +-------+
	                                 \      :       :
	                                  \     +-------+
	                                   ---->| A->1  |
	                                        +-------+
	                                        :       :

然而,如果在CPU2上的load A和load B之间放置一个read屏障:

	CPU 1			CPU 2
	=======================	=======================
		{ A = 0, B = 9 }
	STORE A=1
	<write barrier>
	STORE B=2
				LOAD B
				<read barrier>
				LOAD A

CPU1上的偏序关系将能被CPU2正确感知到:

	+-------+       :      :                :       :
	|       |       +------+                +-------+
	|       |------>| A=1  |------      --->| A->0  |
	|       |       +------+      \         +-------+
	| CPU 1 |   wwwwwwwwwwwwwwww   \    --->| B->9  |
	|       |       +------+        |       +-------+
	|       |------>| B=2  |---     |       :       :
	|       |       +------+   \    |       :       :       +-------+
	+-------+       :      :    \   |       +-------+       |       |
	                             ---------->| B->2  |------>|       |
	                                |       +-------+       | CPU 2 |
	                                |       :       :       |       |
	                                |       :       :       |       |
	  At this point the read ---->   \  rrrrrrrrrrrrrrrrr   |       |
	  barrier causes all effects      \     +-------+       |       |
	  prior to the storage of B        ---->| A->1  |------>|       |
	  to be perceptible to CPU 2            +-------+       |       |
	                                        :       :       +-------+

为了更彻底说明这个问题,考虑read屏障的两侧都有load A将发生什么:

	CPU 1			CPU 2
	=======================	=======================
		{ A = 0, B = 9 }
	STORE A=1
	<write barrier>
	STORE B=2
				LOAD B
				LOAD A [first load of A]
				<read barrier>
				LOAD A [second load of A]

即使两个load A都发生在loadB之后,它们仍然可能获得不同的值:

	+-------+       :      :                :       :
	|       |       +------+                +-------+
	|       |------>| A=1  |------      --->| A->0  |
	|       |       +------+      \         +-------+
	| CPU 1 |   wwwwwwwwwwwwwwww   \    --->| B->9  |
	|       |       +------+        |       +-------+
	|       |------>| B=2  |---     |       :       :
	|       |       +------+   \    |       :       :       +-------+
	+-------+       :      :    \   |       +-------+       |       |
	                             ---------->| B->2  |------>|       |
	                                |       +-------+       | CPU 2 |
	                                |       :       :       |       |
	                                |       :       :       |       |
	                                |       +-------+       |       |
	                                |       | A->0  |------>| 1st   |
	                                |       +-------+       |       |
	  At this point the read ---->   \  rrrrrrrrrrrrrrrrr   |       |
	  barrier causes all effects      \     +-------+       |       |
	  prior to the storage of B        ---->| A->1  |------>| 2nd   |
	  to be perceptible to CPU 2            +-------+       |       |
	                                        :       :       +-------+

但是,在read屏障完成之前,CPU1对A的更新就可能被CPU2看到:

	+-------+       :      :                :       :
	|       |       +------+                +-------+
	|       |------>| A=1  |------      --->| A->0  |
	|       |       +------+      \         +-------+
	| CPU 1 |   wwwwwwwwwwwwwwww   \    --->| B->9  |
	|       |       +------+        |       +-------+
	|       |------>| B=2  |---     |       :       :
	|       |       +------+   \    |       :       :       +-------+
	+-------+       :      :    \   |       +-------+       |       |
	                             ---------->| B->2  |------>|       |
	                                |       +-------+       | CPU 2 |
	                                |       :       :       |       |
	                                 \      :       :       |       |
	                                  \     +-------+       |       |
	                                   ---->| A->1  |------>| 1st   |
	                                        +-------+       |       |
	                                    rrrrrrrrrrrrrrrrr   |       |
	                                        +-------+       |       |
	                                        | A->1  |------>| 2nd   |
	                                        +-------+       |       |
	                                        :       :       +-------+

如果load B == 2,可以保证第二次load A总是等于 1。但是不能保证第一次load A的值,A == 0或A == 1都可能会出现。



酷毙
1

雷人

鲜花

鸡蛋
1

漂亮

刚表态过的朋友 (2 人)

  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部