Bee*_*ope 6 linux x86 memory-model memory-barriers
也许这只是我,但man 2页面中的例子membarrier似乎毫无意义.
基本上,membarrier()是一个异步内存屏障,给定两个协调的代码段(让我们调用然后快速路径和慢速路径)允许您将屏障的所有硬件成本移动到慢速路径,并且只保留快速路径编译屏障1.有几种不同的方法可以完成这种membarrier行为,例如向每个涉及的处理器发送IPI或等待每个处理器上运行的代码被取消调度 - 但具体的实现细节在这里并不重要.
现在,这是手册页中给出的示例转换:
static volatile int a, b;
static void
fast_path(void)
{
int read_a, read_b;
read_b = b;
asm volatile ("mfence" : : : "memory");
read_a = a;
/* read_b == 1 implies read_a == 1. */
if (read_b == 1 && read_a == 0)
abort();
}
static void
slow_path(void)
{
a = 1;
asm volatile ("mfence" : : : "memory");
b = 1;
}
Run Code Online (Sandbox Code Playgroud)
(省略了一些syscall和init样板)
static volatile int a, b;
static void
fast_path(void)
{
int read_a, read_b;
read_b = b;
asm volatile ("" : : : "memory");
read_a = a;
/* read_b == 1 implies read_a == 1. */
if (read_b == 1 && read_a == 0)
abort();
}
static void
slow_path(void)
{
a = 1;
membarrier(MEMBARRIER_CMD_SHARED, 0);
b = 1;
}
Run Code Online (Sandbox Code Playgroud)
这里slow_path正在进行两次写入(a然后b)由屏障分隔,并且fast_path正在进行两次读取(b然后a)也被屏障隔开.
但是,x86内存模型不允许加载或存储重新排序!据我所知,membarrier()在这种情况下mfence根本不需要,原始代码中也不需要.似乎简单的编译器障碍在两个地方都足够了2.
实际上有意义的一个例子,IMO应该有一个存储,后跟一个加载,由快速路径中的一个屏障隔开.
我错过了什么吗?
1编译器屏障防止编译器在其上移动加载或存储(并且取决于实现可能会强制某些寄存器值到内存),但不会发出任何类型的原子操作或内存栅栏,因此请避免经常命令 -这些指令中固有的幅度减慢.
2当然,在可能发生负载重新排序的较弱平台上,示例可能有意义,但示例显式为x86,membarrier()并且仅在x86上实现.
你是对的.在x86上,这种特殊用途membarrier()完全没用.事实上,这个确切的例子是英特尔SDM中第一个用来说明x86内存排序规则的例子:
小智 5
我正在提交对此联机帮助页的修复,以改用 Dekker 示例。参见https://lkml.org/lkml/2017/9/18/779
顺便说一下,membarrier 系统调用不是特定于 x86 的,而是现在大多数 Linux 架构上实现的。
感谢您的反馈!
马蒂厄
| 归档时间: |
|
| 查看次数: |
268 次 |
| 最近记录: |