现代 CPU中指令的执行次序并不一定严格按顺序执行的,没有相关性的指令可以打乱次序执行,以充分利用 CPU的指令流水线,提高执行速度。另外,编译器也会对指令进行优化,例如:调整指令顺序来利用CPU的指令流水线。这些优化方式,绝大部分情况工作的很好,但是在一些比较复杂的情况可能出问题,例如执行同步代码时就有可能因为这种优化导致同步原语之后的指令在同步原语前执行。内存屏障和编译屏障就是用来告诉CPU和编译器停止优化的方法。
编译屏障是使用伪指令“memory”告诉编译器不能把“memory”前后的代码混淆在一起,这时“memory”也起到一种优化屏障的作用。内存屏障是在代码中插入特殊指令,如arm中的dmb,dsb和isb指令,x86中的sfence,lfence和mfence指令。CPU遇到这些特殊指令,要等待前面指令执行完成了,才执行后面的指令。这些指令的作用好像是一道屏障把前后指令隔开,这样就防止了CPU把前后两段指令颠倒执行。
1.
-
dsb :数据同步屏障指令。它的作用是等待所有之前的指令完成后再执行后面的指令。 -
dmb :数据内存屏障指令。它的作用是等待前面访问内存的指令完成后再执行后面访问内存的指令。
-
isb :指令同步屏障。它的作用是等待流水线中所有指令执行完成后再执行后面的指令。
2.
-
sfence :存储屏障指令。它的作用是等待前面写内存的指令完成后再执行后面写内存的指令。 -
lfence :读取屏障指令。它的作用是等待前面读取内存的指令完成后再执行后面读取内存的指令。
-
mfence :混合屏障指令。它的作用是等待前面读写内存的指令完成后再执行后面读写内存的指令。
下面看看Android4.4中内存屏障和编译屏障函数是如何实现的:
1.
1)
void android_compiler_barrier()
{
}
编译屏障的实现只是使用了伪指令memory。
2)
void android_memory_barrier()
{
#if ANDROID_SMP == 0
#else
#endif
}
void android_memory_store_barrier()
{
#if ANDROID_SMP == 0
#else
#endif
}
内存屏障的函数中使用了宏ANDROID_SMP。它的值为0表示是单CPU,这种情况下只使用编译屏障就可以了。在多CPU情况下,同时使用了内存屏障指令“dmb”,和编译屏障的伪指令“memory”。函数android_memory_store_barrier中的dmb指令使用了选项st,表示要等待前面所有存储内存的指令执行完后再执行后面的存储内存的指令。
2.
1)
void android_compiler_barrier(void)
{
}
和arm平台下一样,编译屏障的实现只是使用了伪指令memory。
2)
#if ANDROID_SMP == 0
void android_memory_barrier(void)
{
}
void android_memory_store_barrier(void)
{
}
#else
void android_memory_barrier(void)
{
}
void android_memory_store_barrier(void)
{
}
#endif
x86平台也一样,如果是单CPU,内存屏障的实现只使用了编译屏障。在多CPU情况下,函数android_memory_barrier使用了内存屏障指令“mfence”,对读写内存的情况都进行了屏障。但是android_memory_store_barrier函数只使用了编译屏障,只是因为Intel的cpu不对写内存的指令重新排序。所以不需要内存屏蔽指令。