linux上的内存屏障和atomic-t



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

uint64_t used;
uint64_t blocked;
used = atomic64_read(&g_variable->used);       //#1
barrier();                                     //#2
blocked = atomic64_read(&g_variable->blocked); //#3

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

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

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

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

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

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指令对该值执行所有操作。

在64位x86_64处理器上,64位类型的对齐读取是原子的,可以用MOV指令来完成,所以只需要普通读取——volatile的使用只是为了确保编译器真正进行读取,而不会缓存以前的值。

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

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

x86 CPU不进行读取后重新排序,因此足以防止编译器进行任何重新排序。在PowerPC等其他平台上,情况会大不相同。

相关内容

  • 没有找到相关文章

最新更新