内存屏障和linux上的atomic_t

Cha*_*ang 12 linux concurrency multithreading atomic barrier

最近,我正在阅读一些Linux内核空间代码,我看到了这一点

uint64_t used;
uint64_t blocked;

used = atomic64_read(&g_variable->used);       //#1
barrier();                                     //#2
blocked = atomic64_read(&g_variable->blocked); //#3
Run Code Online (Sandbox Code Playgroud)

这段代码片段的语义是什么?是否确保#1在#3之前执行#1.但是,我有点困惑,因为

#A在64位平台中,atomic64_read宏被扩展为

used = (&g_variable->used)->counter           // where counter is volatile.
Run Code Online (Sandbox Code Playgroud)

在32位平台中,它被转换为使用锁cmpxchg8b.我假设这两个语义具有相同的语义,对于64位版本,我认为这意味着:

  1. 全有或全无,我们可以排除地址未对齐且字大小大于CPU本机字大小的情况.
  2. 没有优化,强制CPU从内存位置读取.

atomic64_read没有保留读取顺序的语义!看到这个

#B屏障宏定义为

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

wiki中可以防止gcc编译器重新排序读写.

我感到困惑的是它如何禁用CPU的重新排序优化?另外,我认为屏障宏是完全围栏吗?

Ant*_*ams 8

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

在64位x86_64处理器上,64位类型的对齐读取是原子的,并且可以使用MOV指令完成,因此只需要进行普通读取 - 使用volatile只是为了确保编译器实际执行读取,并且不缓存先前的值.

至于读取顺序,您引用的内联汇编程序可确保编译器以正确的顺序发出指令,这是x86/x86_64 CPU上所需的全部内容,前提是写入正确排序.LOCKed写在x86上有一个总排序; 普通MOV写入提供"因果一致性",因此如果线程A x=1那么y=2,如果线程B读取,y==2则后续读取x将看到x==1.

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

  • @Anthony Williams:是的cpu必须支持XMM,我们也可以直接从xmm 128位寄存器加载到32位寄存器,如:**movq xmm0,qword [Source]; movd eax,xmm0; pshufd xmm0,xmm0,1; movd edx,xmm0**.通过这种方式,在32位x86模式下也可以进行128位原子读/写. (3认同)