锁定前缀的作用范围是什么?

3
据说以“lock”为前缀的汇编指令是原子性的。我想知道“lock”是否只能影响一个汇编指令;汇编指令本身是否不具备原子性?
以下是Linux内核中原子函数的示例:
static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
 { unsigned char c;
 __asm__ __volatile__(
 LOCK "subl %2,%0; sete %1" 
    :"=m" (v->counter), "=qm" (c) 
     :"ir" (i), "m" (v->counter) : "memory"); 
return c; }

在这个例子中,subl和sete可以被打断吗?
3个回答

9

锁定前缀只影响单条指令。

当指令修改被多个 CPU 共享的内存时,指令就不再是原子性的了。如果修改涉及到读取一个内存操作数、对它执行一些操作(比如 AND、XOR、INC 等),然后将其写回,则其他 CPU 不会将其视为原子操作。锁定前缀“锁定”了内存位置,使得这 3 步(读取、修改、写回)看起来像一个整体,即其他 CPU 只能观察到锁定指令之前和之后的内容。

请参阅 Intel 或 AMD 的官方 CPU 文档。

编辑:在您新增的示例中,如果我们讨论的是中断,则这两条指令都不能被中断。中断发生在整个指令之间。锁定前缀使 sub 指令具有原子性。sete 指令并不是旨在实现原子性,而是将 ZF 标志转换为零或非零整数值。


我已经在这个问题中添加了一个例子。 - venus.w
我是说这两个指令之间可能会发生中断吗? - venus.w
可以的。有什么问题吗? - Alexey Frunze
AlexeyFrunze: 我猜@venus.w担心两个指令之间发生中断可能会破坏标志寄存器并导致错误的结果。但实际上,当中断返回时,标志寄存器将被恢复。 - Lai Jiangshan
如果编译器选择内存,sete(mem)将是原子存储。它不是RMW,只是一个字节存储(从FLAGS派生的值),因此它是微不足道的原子操作; x86保证在所有情况下单字节存储的原子性。(它不是与“lock sub”相同事务的一部分,并且正在操作一个未共享的变量,因此对于这种用例来说完全不相关。针对窄对齐的纯负载和纯存储的原子性相对简单,在大多数CPU中基本上可以免费实现。) - Peter Cordes

4

与在其他CPU核心上同时运行的其他指令不原子的指令。 LOCK前缀的作用是锁定总线,使其他核心在锁定指令运行时无法访问内存,从而防止两个核心尝试同时访问同一位置时发生冲突。


它的工作方式就像锁定共享总线一样,但实际上它只需要使当前核心保持在RMW的加载和存储部分之间的缓存行上拥有MESI独占所有权(也称为“缓存锁”)。多个核心可以并行运行对不同缓存行的原子RMW操作,而无需减速或离核心流量,只需命中其L1d缓存即可。 - Peter Cordes
就像LOCK前缀与MESI协议?中所述。 - Peter Cordes

3
只有极少数的汇编指令是原子性的。x86架构基本上是一个CISC架构,这意味着单个指令可以完成很多工作,其中一个指令可以将内存地址中的值加载到寄存器中,对其进行操作,然后将结果存回内存。这是一个复杂的操作,需要很多时间和CPU周期来执行;为了提高性能,许多这样的指令被流水线化,并且它们的执行顺序是交织在一起的。
基本上,汇编语言就像任何其他语言一样。特别是在谈论CISC架构时,有许多“高级”结构和操作可以在单个命令中执行,除非在指令的文档或使用“lock”修饰符明确指定,否则不能保证原子执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接