最近,我正在阅读一些Linux内核空间代码,我看到了
uint64_t used; uint64_t blocked; used = atomic64_read(&g_variable->used); //#1 barrier(); //#2 blocked = atomic64_read(&g_variable->blocked); //#3
该代码段的语义是什么?是否确保#1在#3之前由#2执行。但是我有点乱,因为
#A 在64位平台上,atomic64_read宏扩展为
used = (&g_variable->used)->counter // where counter is volatile.
在32位平台中,将其转换为使用锁 cmpxchg8b 。我认为这两个具有相同的语义,对于64位版本,我认为这意味着:
atomic64_read没有保留读取顺序的语义!!! 看到这个
#B 的 屏障 宏定义为
/* Optimization barrier */ /* The "volatile" is due to gcc bugs */ #define barrier() __asm__ __volatile__("": : :"memory")
在Wiki中,这只是防止 gcc编译器 重新排列读写顺序。
我很困惑的是它如何禁用CPU的重新排序优化?另外,我可以认为屏障宏是完整的栅栏吗?
32位x86处理器不能为64位类型提供简单的原子读取操作。在此类处理“普通”寄存器的CPU上,对64位类型唯一的原子操作是LOCK CMPXCHG8B,这就是为什么在此使用它的原因。另一种选择是使用MOVQ和MMX / XMM寄存器,但是这需要了解FPU状态/寄存器,并且需要使用MMX / XMM指令完成对该值的所有操作。
LOCK CMPXCHG8B
MOVQ
在64位x86_64处理器上,对64位类型的对齐读取是原子的,并且可以通过一条MOV指令完成,因此仅需要进行普通读取— 的使用volatile只是为了确保编译器实际进行读取,并且不缓存先前的值。
MOV
volatile
至于读取顺序,您引用的内联汇编程序可确保编译器以正确的顺序发出指令,这是x86 / x86_64 CPU所需要的,只要写入顺序正确即可。LOCKed在x86上的写入具有总顺序;普通MOV写提供了“因果一致性”,因此,如果线程A做到了,x=1那么y=2如果线程B进行了读取,y==2则随后的读取x将看到x==1。
LOCK
x=1
y=2
y==2
x
x==1
在IA-64中,的PowerPC,SPARC,和具有更宽松的存储器模型其它处理器很可能有更多的atomic64_read()和barrier()。
atomic64_read()
barrier()