Linux上的内存屏障和atomic_t
Posted
技术标签:
【中文标题】Linux上的内存屏障和atomic_t【英文标题】:memory barrier and atomic_t on linux 【发布时间】:2011-09-27 04:21:04 【问题描述】:最近在看一些Linux内核空间的代码,看到了这个
uint64_t used;
uint64_t blocked;
used = atomic64_read(&g_variable->used); //#1
barrier(); //#2
blocked = atomic64_read(&g_variable->blocked); //#3
这段代码sn-p的语义是什么?它是否确保 #1 在 #3 之前由 #2 执行。 但我有点困惑,因为
#A在64位平台,atomic64_read宏扩展为
used = (&g_variable->used)->counter // where counter is volatile.
在 32 位平台中,转换为使用锁 cmpxchg8b。我假设这两个具有相同的语义,对于 64 位版本,我认为这意味着:
-
all-or-nothing,我们可以排除地址未对齐且字长大于 CPU 原生字长的情况。
无优化,强制 CPU 从内存位置读取。
atomic64_read 没有保留读取顺序的语义!!!见this
#B barrier 宏定义为
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")
来自wiki this 只是阻止gcc 编译器 重新排序读取和写入。
我很困惑的是它如何禁用 CPU 的重新排序优化?另外,我可以认为屏障宏是全栅栏吗?
【问题讨论】:
只有我一个人,或者这个问题可以压缩为“这个barrier()宏是如何工作的?” ? 我认为将atomix...
考虑在内很重要;也就是说——当 not 使用atomic...
访问方法时是否存在语义差异?它是否取决于内存模型(强与弱)?一个或另一个是否意味着缓存刷新?等等等等。
@ptx,atomix是什么意思?任何参考
@Nicholas,也许,但这个简单的问题可能会因为没有表现出研究努力而被否决。
【参考方案1】:
32 位 x86 处理器不为 64 位类型提供简单的原子读取操作。在此类 CPU 上处理“普通”寄存器的 64 位类型上唯一的原子操作是 LOCK CMPXCHG8B
,这就是这里使用它的原因。另一种方法是使用 MOVQ
和 MMX/XMM 寄存器,但这需要了解 FPU 状态/寄存器,并且要求对该值的所有操作都使用 MMX/XMM 指令完成。
在 64 位 x86_64 处理器上,64 位类型的对齐读取是原子的,可以使用 MOV
指令完成,因此只需要普通读取 --- 使用 volatile
只是为了确保编译器确实进行了读取,并且不缓存以前的值。
至于读取顺序,您引用的内联汇编程序可确保编译器以正确的顺序发出指令,这就是 x86/x86_64 CPU 所需的全部,前提是写入顺序正确。 LOCK
ed 在 x86 上的写入具有总排序;普通的MOV
写入提供了“因果一致性”,因此如果线程 A 执行 x=1
然后 y=2
,如果线程 B 读取 y==2
然后后续读取 x
将看到 x==1
。
在 IA-64、PowerPC、SPARC 和其他具有更宽松内存模型的处理器上,atomic64_read()
和 barrier()
可能会更多。
【讨论】:
不正确:奔腾处理器(以及之后的更新处理器)保证以下附加内存操作将始终以原子方式执行: • 读取或写入在 64 位边界上对齐的四字LOCK
是 read-modify-write 所必需的,例如原子递增/递减或 CAS,不用于读取,不用于写入。正如 GJ 指出的那样,这些在对齐的四字(本身,不是组合)上是原子的。
@GJ:非英特尔 x86 CPU 也是如此吗? (我并不是说你错了,只是可能需要考虑非英特尔设备)。
@GJ:是的,你是对的。但是,除了CMPXCHG8B
和MOVQ
指令之外,没有指令可以在 32 位模式下进行加载。后者是一条 MMX/SSE 指令,要求您了解 FPU 状态和寄存器可用性。
@Anthony Williams:是的,cpu 必须支持 XMM,我们也可以直接从 xmm 128 位寄存器加载到 32 位寄存器中,例如:movq xmm0, qword [Source]; movd eax, xmm0; pshufd xmm0, xmm0, 1; movd edx, xmm0。这样在 32 位 x86 模式下也可以进行 128 位原子读/写。【参考方案2】:
x86 CPU 不执行 read-after-read 重新排序,因此足以防止编译器进行任何重新排序。在 PowerPC 等其他平台上,情况会大不相同。
【讨论】:
以上是关于Linux上的内存屏障和atomic_t的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理优化内存屏障 ④ ( 处理器内存屏障 | 八种处理器内存屏障 | 通用内存屏障 | 写内存屏障 | 读内存屏障 | 数据依赖屏障 | 强制性内存屏障 |SMP内存屏障 )
Linux 内核 内存管理优化内存屏障 ② ( 内存屏障 | 编译器屏障 | 处理器内存屏障 | 内存映射 I/O 写屏障 )
Linux 内核 内存管理优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )