内存屏障

维基百科,自由的百科全书
跳转至: 导航搜索

内存屏障,也称内存栅栏内存栅障屏障指令等, 是一类同步屏障指令,使得CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

大多数现代计算机为了提高性能而采取乱序执行,这使得内存屏障成为必须。

语义上,内存屏障之前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。因此,对于敏感的程序块,写操作之后、读操作之前可以插入内存屏障。

底层体系结构相关的原语[编辑]

内存屏障是底层原语,是内存排序的一部分,在不同体系结构下变化很大而不适合推广。需要认真研读硬件的手册以确定内存屏障的办法。 x86指令集中的内存屏障指令是:

lfence (asm), void _mm_lfence(void) 读操作屏障
sfence (asm), void _mm_sfence(void)[1]写操作屏障
mfence (asm), void _mm_mfence(void)[2]读写操作屏障

常见的x86/x64,通常使用lock指令前缀加上一个空操作来实现,注意当然不能真的是nop指令,但是可以用来实现空操作的指令其实是很多的,比如Linux中采用的

 addl $0, 0(%esp)

多线程编程与内存可见性[编辑]

多线程程序通常使用高层程序设计语言中的同步原语,如Java.NET Framework, 或者APIpthreadWindows API。因此一般不需要明确使用内存屏障。

内存可见性问题,主要是高速缓存与内存的一致性问题。一个处理器上的线程修改了某数据,而在另一处理器上的线程可能仍然使用着该数据在专用cache中的老值,这就是可见性出了问题。解决办法是令该数据为volatile属性,或者读该数据之前执行内存屏障。

乱序执行与编译器重排序优化的比较[编辑]

C与C++语言中,volatile关键字意图允许内存映射的I/O操作。这要求编译器对此的数据读写按照程序中的先后顺序执行,不能对volatile内存的读写重排序。因此关键字volatile并不保证是一个内存屏障。[3]

编译器内存屏障[编辑]

编译器会对生成的可执行代码做一定优化,造成乱序执行甚至省略(不执行)。gcc编译器在遇到内嵌汇编语句

asm volatile("" ::: "memory");

将以此作为一条内存屏障,重排序内存操作。即此语句之前的各种编译优化将不会持续到此语句之后。也可用内建的__sync_synchronize

Microsoft Visual C++的编译器内存屏障为:

_ReadWriteBarrier() MemoryBarrier()

Intel C++编译器的内存屏障为:

__memory_barrier()

外部链接[编辑]

参考文献[编辑]