内存屏障和atomic_t在Linux上

最近,我正在阅读一些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_readmacros被扩展为

 used = (&g_variable->used)->counter // where counter is volatile. 

在32位平台上,它被转换为使用lockingcmpxchg8b 。 我假设这两个语义相同,对于64位版本,我认为这意味着:

  1. 我们可以排除地址不alignment,字大小大于CPU本地字大小的情况。
  2. 没有优化 ,强制CPU从内存位置读取。

atomic64_read没有语义保存阅读顺序! 看到这个

#B 屏障macros被定义为

 /* Optimization barrier */ /* The "volatile" is due to gcc bugs */ #define barrier() __asm__ __volatile__("": : :"memory") 

从维基这只是防止gcc编译器重新sorting读写。

我感到困惑的是如何禁用CPU的重新sorting优化? 另外,我可以认为障碍macros是完整的篱笆吗?

Solutions Collecting From Web of "内存屏障和atomic_t在Linux上"

32位x86处理器不提供64位类型的简单原子读取操作。 在处理“普通”寄存器的CPU上,64位类型的唯一的原子操作是LOCK CMPXCHG8B ,这就是为什么在这里使用它的原因。 另一种方法是使用MOVQ和MMX / XMM寄存器,但是需要知道FPU状态/寄存器,并且要求所有对该值的操作都通过MMX / XMM指令完成。

在64位x86_64处理器上,64位类型的对齐读取是原子性的,可以用MOV指令完成,所以只需要简单的读取 – volatile的使用只是为了确保编译器实际执行读取,并且不缓存先前的值。

至于读取顺序,您引用的内联汇编程序确保编译器以正确的顺序发出指令,这是x86 / x86_64 CPU上所需的所有操作,前提是写入顺序正确。 在x86上的LOCK编写有一个总的顺序; 普通的MOV写操作提供了“因果一致性”,所以如果线程A执行x=1那么y=2 ,如果线程B读取y==2那么随后的x读取x==1

在IA-64,PowerPC,SPARC和其他具有更宽松内存模型的处理器上,可能还有atomic64_read()barrier()

x86 CPU不会执行读取后读取重新排序,所以防止编译器执行任何重新排序就足够了。 在PowerPC等其他平台上,情况会有所不同。