Y. *_* A. 2 c x86 gcc linux-kernel memory-barriers
为什么我们需要定义两种具有相同实现的障碍?
#if defined(__x86_64) || defined(__i386__)
#define read_barrier() __asm__ __volatile__("":::"memory")
#define write_barrier() __asm__ __volatile__("":::"memory")
#else
Run Code Online (Sandbox Code Playgroud)
真正的答案是:因为 x86 的内存模型已经足够强大,阻塞编译时重新排序对于加载或存储排序就足够了;运行时重新排序已被硬件阻止。
这些只是通过一段内联汇编产生的通用编译时障碍,如果使用,可以防止 GCC 重新排序指令。这在另一篇文章中解释得很好。使用这个“技巧”可以实现的目标通常也可以使用 Cvolatile限定符来实现。
请注意,Linux 内核不会在代码中的任何地方使用这些特定的宏,它们只是为io_uring用户空间测试工具定义的两个宏。它肯定会asm volatile ("" ::: "memory")在需要的地方使用,但名称不同(例如smp_rmb(),smp_wmb())。
86的存储器模型使得sfence与lfence对CPU之间的通信完全无用; 阻止编译时重新排序就足够了:请参阅英特尔内存模型是否使 SFENCE 和 LFENCE 冗余?
smp_mb() 是一个完整的障碍,确实需要一个实际的 asm 指令,以及阻止编译时重新排序。
x86 确实有一些用于只读和只写“真实”(运行时)内存屏障的内存屏障 asm 指令。它们是sfence(存储栅栏)、lfence(加载栅栏)和mfence(内存栅栏 = 满栅栏)。
mfence序列化读取和写入(完全屏障),而其他仅序列化两者之一(读取或写入又名加载或存储)。关于内存排序的维基百科页面在解释这些含义方面做得很好。lfence对于movntdqa来自 WC 内存的弱排序加载,实际上阻止了 LoadStore 重新排序,而不仅仅是 LoadLoad 。已经不允许从其他内存类型重新排序其他类型的负载,因此几乎没有任何理由实际lfence用于内存排序,而不是阻止乱序执行的其他效果。
该内核使用的内存壁垒那些实际的汇编指令在I / O代码,例如mb(),rmb()和wmb() 其扩大确切地mfence,lfence,sfence,和其他人(例如)。
sfence并且lfence在大多数情况下可能是矫枉过正,例如围绕 MMIO 到强有序 UC 内存。写入 WC 内存实际上可能需要一个围栏。但与 I/O 相比,它们并不算太慢,否则在某些情况下可能会出现问题,因此 Linux 采取了安全的方法。
除此之外,x86 具有不同类型的读/写屏障,它们也可能更快(例如我上面链接的那个)。请参阅以下答案以了解有关使用任一mfence或虚拟locked 指令的完整障碍(C11 称为顺序一致性)的更多信息:
| 归档时间: |
|
| 查看次数: |
661 次 |
| 最近记录: |