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