c - 单核上的 x86_64 内存屏障

标签 c x86-64 memory-barriers

在 x86_64 上,英特尔文档第 8.2.3.2 节第 3A 卷说:

The Intel-64 memory-ordering model allows neither loads nor stores to be reordered with the same kind of operation. That is, it ensures that loads are seen in program order and that stores are seen in program order

我需要确保在写入内存地址时不会重新排列变量。

我想避免原子 xchg 因为它涉及的成本很高。在我的应用程序中,读取该值的其他 CPU 知道如何处理不完整状态。

部分代码:

cli();
compiler_unoptimization(); // asm volatile("":::"memory")
volatile uint *p = 0x86648664; // address doesn't matter
*p = 1;
... // some code here
*p = 0;
sti();

那么,我的假设是否正确:

  • 1) cpu 不会在 *p = 1 之前使 *p = 0,不需要 sfence

  • 2) 编译器(gcc 或 clang)不会使用 asm 技巧(这里需要,对吧?)来反转 p 的写法。

最佳答案

虽然 C 标准保证按顺序发出对 volatile 对象的访问,但与非 volatile 对象相比,它并不保证这一点。

你在这里有两个访问volatile,所以编译器必须按顺序生成这些,但省略号中的任何内容都可以自由移动**除非这些是volatile,也是!

此外,volatile 并不意味着硬件将按C 标准 的顺序执行。这将通过 CPU 的适当屏障来保证,但是 - 根据架构和屏障 - 它可能不足以满足其余硬件(缓存,总线,内存系统等)。

对于 x86,顺序是有保证的(虽然不是典型的:许多 RISC,例如 ARM 和 PPC 更宽松,因此需要更仔细地编写代码)。由于您仅引用单个 CPU,并且 volatile 在此处没有副作用,因此内存系统不相关。所以你在这里是安全的。

对于内存映射外设和多处理器来说,事情要复杂得多,也就是说,如果你有超出单个 CPU 的副作用。简单示例:第一次写入可能不会通过 CPU 缓存,因此读取同一内存页的任何内容可能只会看到第二次写入或根本没有。 volatile 在这里还不够,您需要原子访问和(可能的)屏障。

对于您的代码,您可以使省略号 volatile 中的所有变量(低效),或者在它们周围添加编译器屏障(在 *p = 1 之后;*p = 0; 之前)。这样编译器就不会将指令移动到障碍之外。

最后:volatile 不保证原子访问。因此,*p 可能不是由单个指令写入的。 (我不会过分强调这一点,因为我假设 uintunsigned int,在 32 位或 64 位 x86 目标上通常是 32 位,但这将是一个问题对于 8 位或 16 位 CPU。)为了安全起见,请使用 _Atomic 类型(自 C11 起)。

PS:像 uint 这样的类型。标准类型 unsigned 并没有多打多少字,但每个人都能立刻明白你的意思。如果您需要特定宽度,请使用 stdint.h 类型。在这里,您甚至应该使用 _Bool/bool,因为您似乎只有一个 true/false 标志.

请注意,所有这些功能也可用于低级代码。特别是 _Atomic(也请参见 stdatomic.h)用于此类用途,通常不需要任何特殊库。如果它们也可以原子存储,它们的用法通常不会比非限定类型复杂(还有一些宏可以指示特定类型是否为原子)。

关于c - 单核上的 x86_64 内存屏障,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33825120/

相关文章:

使用 `clone()` 创建子进程。为什么 `wait()` 不等待它终止?

C 重复数组

c# - 从 C# 调用 libyahoo2 函数

c - 使用另一个字符串作为分隔符将字符串分解为标记列表?

linux - 使用 gcc 编译 64 位 linux 内核

go - 无锁并发访问变量

Linux 3.0 TCP 堆栈接收缓冲区内核架构

assembly - 处理器是否将数据从 'L1 code' 缓存切换到 'L1 data' 缓存?

c++ - C++ 中的 std::memory_order 究竟提供了哪些栅栏?

c - 内存分配优化