内存屏障 Memery Barrier

来自于在CU的一个讨论:
http://linux.chinaunix.net/bbs/thread-713279-1-1.html
讨论完了给一个总结,有些话是别人说的,有的还是clf的网友的,为了不使文档显得杂乱,都不具名了。
欢迎批评指正!


内核中定义的内存屏障原语有:

#define barrier() __asm__ __volatile__("": : :"memory")
#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)

#ifdef CONFIG_SMP
#define smp_mb() mb()
#define smp_rmb() rmb()
#define smp_wmb() wmb()
#define smp_read_barrier_depends() read_barrier_depends()
#define set_mb(var, value) do { (void) xchg(&var, value); } while (0)
#else
#define smp_mb() barrier()
#define smp_rmb() barrier()
#define smp_wmb() barrier()
#define smp_read_barrier_depends() do { } while(0)
#define set_mb(var, value) do { var = value; barrier(); } while (0)
#endif


1). smp_xxx()和xxx()的区别

为了给其它CPU也提供相关的barrier宏。 例如x86的rmb()是用了lfence指令,但其它CPU不能用这个指令。


2). 关于barrier()宏,jkl大师是这么说的:

CPU越过内存屏障后,将刷新自己对存储器的缓冲状态。这条语句实际上不生成任何代码,但可使gcc在
barrier()之后刷新寄存器对变量的分配。

也就是说,barrier()宏只约束gcc编译器,不约束运行时的CPU行为。 举例:

1 int a = 5, b = 6;
2 barrier();
3 a = b;

在line 3,GCC不会用存放b的寄存器给a赋值,而是invalidate b的Cache line,重新读内存中的b值,赋值给a。


3). mb() vs. rmb() vs. wmb()

rmb()不允许读操作穿过内存屏障;wmb()不允许写操作穿过屏障;而mb()二者都不允许。

看IA32上wmb()的定义:
#ifdef CONFIG_X86_OOSTORE
#define wmb() alternative("lock;addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM);
#else
#define wmb() __asm__ __volatile__ ("": : :"memory");
#endif

Intel和AMD都没有在IA32 CPU中实现乱序写(Out-Of-Order Store),所以wmb()定义为空操作,不约束CPU行为;但
有些IA32 CPU厂商实现了OOO Store,所以就有了使用sfence的那个wmb()实现。


4). 内存屏障的体系结构语义

4.1) 只有一个主体(CPU或DMA控制器)访问内存时,无论如何也不需要barrier;但如果有两个或更多主体访问内存,且
其中有一个在观测另一个,就需要barrier了。

4.2) IA32 CPU调用有lock前缀的指令,或者如xchg这样的指令,会导致其它的CPU也触发一定的动作来同步自己的Cache。
CPU的#lock引脚链接到北桥芯片(North Bridge)的#lock引脚,当带lock前缀的执行执行时,北桥芯片会拉起#lock
电平,从而锁住总线,直到该指令执行完毕再放开。 而总线加锁会自动invalidate所有CPU对 _该指令涉及的内存_
的Cache,因此barrier就能保证所有CPU的Cache一致性。

4.3) 接着解释。
lock前缀(或cpuid、xchg等指令)使得本CPU的Cache写入了内存,该写入动作也会引起别的CPU invalidate其Cache。
IA32在每个CPU内部实现了Snoopying(BUS-Watching)技术,监视着总线上是否发生了写内存操作(由某个CPU或DMA控
制器发出的),只要发生了,就invalidate相关的Cache line。 因此,只要lock前缀导致本CPU写内存,就必将导致
所有CPU去invalidate其相关的Cache line。

两个地方可能除外:
-> 如果采用write-through策略,则根本不存在缓存一致性问题(Linux对全部内存采用write-back策略);
-> TLB也是Cache,但它的一致性(至少在IA32上)不能通过Snoopying技术解决,而是要发送
INVALIDATE_TLB_VECTOR这个IPI给其它的CPU。

4.4) 进一步解释,MESI协议

包括IA32的许多体系结构的CPU,为了保证缓存一致性,实现了MESI协议。

M: Modified,已修改
E: Exclusive,排他
S: Shared,共享
I: Invalid,无效

IA32 的CPU实现了MESI协议来保证Cache coherence。 CPU的总线监测单元,始终监视着总线上所有的内存写操作,
以便随时调整自己的Cache状态。

-> Modified。 本CPU写,则直接写到Cache,不产生总线事物;其它CPU写,则不涉及本CPU的Cache,其它CPU
读,则本CPU需要把Cache line中的数据提供给它,而不是让它去读内存。

-> Exclusive。只有本CPU有该内存的Cache,而且和内存一致。 本CPU的写操作会导致转到Modified状态。

-> Shared。 多个CPU都对该内存有Cache,而且内容一致。任何一个CPU写自己的这个Cache都必须通知其它
的CPU。

-> Invalid。 一旦Cache line进入这个状态,CPU读数据就必须发出总线事物,从内存读。


5) 考虑到DMA

5.1). Wirte through策略。 这种情形比较简单。

-> 本CPU写内存,是write through的,因此无论什么时候DMA读内存,读到的都是正确数据。
-> DMA写内存,如果DMA要写的内存被本CPU缓存了,那么必须Invalidate这个Cache line。下次CPU读它,就
直接从内存读。

5.2). Write back策略。 这种情形相当复杂。

-> DMA读内存。被本CPU总线监视单元发现,而且本地Cache中有Modified数据,本CPU就截获DMA的内存读操作,
把自己Cache Line中的数据返回给它。

-> DMA写内存。而且所写的位置在本CPU的Cache中,这又分两种情况:
a@ Cache Line状态未被CPU修改过(即cache和内存一致),那么invalidate该cache line。
b@ Cache Line状态已经被修改过,又分2种情况:

<1> DMA写操作会替换CPU Cache line所对应的整行内存数据,那么DMA写,CPU则invalidate
自己的Cache Line。
<2> DMA写操作只替换Cache Line对应的内存数据的一部分,那么CPU必须捕获DMA写操作的新

数据(即DMA想把它写入内存的),用来更新Cache Line的相关部分。



//

“在line 3,GCC不会用存放b的寄存器给a赋值,而是invalidate b的Cache line,重新读内存中的b值,赋值给a。”

感觉这个是不对的。上面都说了“barrier()宏只约束gcc编译器,不约束运行时的CPU行为”,
而invalidate Cache line要调用wbinvd命令,我对GCC的代码进行grep 没有发现使用wbinvd的地方。



4.2) IA32
CPU调用有lock前缀的指令,或者如xchg这样的指令,会导致其它的CPU也触发一定的动作来同步自
己的Cache。
CPU的#lock引脚链接到北桥芯片(North
Bridge)的#lock引脚,当带lock前缀的执行执行时,北桥芯片会拉起#lock
电平,从而锁住总线,直到该指令执行完毕再放开。 而总线加锁会自动invalidate所有CPU对
_该指令涉及的内存_
的Cache,因此barrier就能保证所有CPU的Cache一致性。 ”

  

   总线加锁的功能是保证程序执行的顺序不乱掉,

一旦加LOCK指令了,CPU会将此指令前的读写操作都串行完成,这最主要的作用是使CPU的预取等无
效了。

在这个串行操作中,MESI协议会起作用。

但保证所有CPU的Cache一致性的是MESI协议,这是硬件上保证的。

barrier是对GCC编译器做约束,是软件层次上的。

“因此barrier就能保证所有CPU的Cache一致性”这种说法是不对的。

  


“4.3) 接着解释。
lock前缀(或cpuid、xchg等指令)使得本CPU的Cache写入了内存,该写入动作也会引起别的CPU
invalidate其Cache。
IA32在每个CPU内部实现了Snoopying(BUS-Watching)技术,监视着总线上是否发生了写内存操作(�
赡掣鯟PU或DMA控
制器发出的),只要发生了,就invalidate相关的Cache line。
因此,只要lock前缀导致本CPU写内存,就必将导致
所有CPU去invalidate其相关的Cache line。 ”

  

lock指令保证程序执行的顺序不乱掉,没有将“本CPU的Cache写入了内存”的功能。 

总线监视功能是由各个CPU的CACHE完成的,

这个功能可以算是MESI协议的实现。MESI保证了SMP下的CACHE一致性。



memory barrier的实质是对内存的操作施加顺序上的约束。

Memory barrier, also known as membar or memory fence, is a class of instructions which
cause a central processing unit (CPU) to enforce an ordering constraint on memory
operations issued before and after the barrier instruction.



  上文源于:http://www.answers.com/topic/memory-barrier



  至于“SMP情况下CACHE的一致性”只是memory
barrier在执行lock,sfence,lfence等指令时的副作用 !!!!,

    “SMP情况下CACHE的一致性”是由MESI来做的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
英国科学家 Dominic Sweetman(经典著作 See MIPS Run 的作者),称 MIPS 为“高效的 RISC 体系结构中最优雅的一种体系结构”。 它是一个双关语: 即是 Microcomputer without Interlocked Pipeline Stages 的缩写,同时又是 Millions of Instructions Per Second 的缩 写。 MIPS 的处理单元是一个五级流水线:Instruction Fetch, Register & Decoder, ALU, Memory 以及 Write back。一开始的 MIPS 是为 32 位系统设计的,实际上,后续的 64 位扩 展,也依然对 32 位的工作模式向下兼容着。一如其他的 RISC 处理器,MIPS 的每条指令长度 是固定的 32bit。(因此,最长的局部跳转指令只能跳转 2 的 26 次方 Byte,也就是 2 的 24 次 方,16777216 条指令) MIPS 有 32 个通用寄存器,编程者可以使用其中除$0 外的所有寄存器暂存数据。$0 寄存 器,在硬件上被设计为永远读出 0。我们可以用$0 寄存器的此特性,实现一些技巧性的编程, 譬如实现 NOP 操作。MIPS 本没有 NOP 指令,但由于对$0 寄存器的写入实际上无意义,可以 作为空操作使用。 事实上,编译器从高级语言(典型如 C/C++)转换为 MIPS 汇编指令时,一般总是遵守一 定的寄存器使用约定。某些寄存器用来从函数中传入和传出参数,存储临时数据,另一些则起特 殊作用,如保存调用函数时的指令地址,或作为堆栈指针等。如果你使用汇编开发,理论上可以 无视这点约定,但是,一般地,遵守这个约定,与人方便,也为己方便。关于寄存器使用的约定, 以后会展开论述。 一如所有的 RISC 处理器,MIPS 没有 CISC 那样复杂多变的寻址方式,统一为 Load/Store 寻址。任何载入和存储操作,都可写为如下形式: lw $1, offset ($2) 这条指令的操作符可以为 Load 或 Store,一次 Load/Store 操作的范围可以为字/半字/ 字节(对应 gcc 的 int, short 和 char)。偏移量是一个带符号的 16bit 整数。两个作为操作数 的寄存器可以是任何通用寄存器。(你可以向$0 写入,但无任何意义,等同于空操作)。对于 64 位模式下,也可以对 double 类型进行操作。注意 Load/Store 都必须对应一个对齐的地址,否 则会引发一个异常(Exception)。 MIPS 支持最多 4 个协处理器。协处理器 CP0 为 CPU 的控制协处理器,是体系结构中必须 实现的。CP1 为浮点处理器。CP2 保留,各生产厂商往往用来实现一些自己的特色功能,例如 RMI 的 Fast Message Ring 等。CP3 原本也是保留共扩展用,但很多 MIPS III 和 MIPS IV 时代的扩展指令集使用了它。对于 CP0,我们会有专门的一段用来讨论。 为适应处理器向多核时代的演进, MIPS 引入了多核操作必要的原子指令 (Atomic operation)、内存屏障(Barrier)等操作。在 SMP 或 AMP 等多核架构中,这些指令是并行计算 同步的保障。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值