x86 CMPXCHG 是原子的吗?如果是,为什么它需要 LOCK?

Is x86 CMPXCHG atomic, if so why does it need LOCK?

Intel documentation

This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically.

我的问题是

  1. 可以CMPXCHG用内存地址操作吗?从文档中似乎不能,但任何人都可以确认只适用于寄存器中的实际值,而不是内存地址吗?

  2. 如果CMPXCHG不是原子的并且必须通过LOCK CMPXCHG(带有LOCK前缀)来实现高级语言级别的CAS,那么目的是什么引入这样的指令吗?

(我是从高级语言的角度来问的。也就是说,如果无锁算法必须在x86平台上翻译成LOCK CMPXCHG,那么它仍然以LOCK为前缀。这意味着无锁算法并不比精心编写的同步锁/互斥锁(至少在 x86 上)更好。这似乎也使裸 CMPXCHG 指令变得毫无意义,因为我想引入它的主要目的是支持这种无锁操作.)

LOCK前缀是为了锁定当前命令的内存访问,使得CPU管道中的其他命令不能同时访问内存。使用 LOCK 前缀,命令的执行不会由于同时执行的其他命令的内存访问而被 CPU 管道中的另一个命令中断。 INTEL手册说:

The LOCK prefix can be prepended only to the following in structions and only to those forms of the instructions where the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source operand is a memory operand, an undefined opcode exception (#UD) may be generated.

您将高级锁与碰巧命名为 LOCK 的低级 CPU 功能混淆了。

无锁算法试图避免的高级锁可以保护执行可能需要任意时间的任意代码片段,因此,这些锁将不得不让线程进入等待状态,直到锁可用,这是一个昂贵的操作,例如意味着维护一个等待线程队列。

这与 CPU LOCK 前缀功能完全不同,后者仅保护单个指令,因此可能仅在该单个指令的持续时间内保持其他线程。由于这是由 CPU 本身实现的,因此不需要额外的软件工作。

因此,开发无锁算法的挑战不在于完全消除同步,它归结为将代码的关键部分减少为单个原子操作,该操作将由 CPU 本身提供.

您真正要问的部分似乎是:

Why isn't the lock prefix implicit for cmpxchg with a memory operand, like it is for xchg (since 386)?

简单的答案(其他人给出的)就是英特尔以这种方式设计的。但这引出了一个问题:

Why did Intel do that? Is there a use-case for cmpxchg without lock?

在单CPU系统上,cmpxchg相对于其他线程或任何其他代码的原子运行ning 在同一个 CPU 核心 上。 (但对于像内存映射 I/O 设备或​​对正常内存进行 DMA 读取的设备这样的“系统”观察者来说不是这样,因此 lock cmpxchg 即使在单处理器 CPU 设计上也是相关的)。 =37=]

上下文切换只能发生在中断上,中断发生在指令之前或之后,而不是在中间。在同一个 CPU 上的任何代码 运行ning 都会将 cmpxchg 视为完全执行或根本不执行 .


例如,Linux 内核通常编译时支持 SMP,因此它使用 lock cmpxchg 用于原子 CAS。但是当在单处理器系统上启动时,它会将 lock 前缀修补到 nop 任何代码被内联的地方,因为 nop cmpxchg 运行s比 lock cmpxchg 快得多。有关详细信息,请参阅此 LWN article about Linux's "SMP alternatives" system。它甚至可以在热插拔第二个 CPU.

之前修补回 lock 前缀

阅读有关单处理器系统上单指令原子性的更多信息, and in on Can num++ be atomic for int num. See ,了解有关原子性如何真正工作/实现读-修改-写指令的更多详细信息,例如 lock cmpxchg


(同样的推理也适用于 cmpxchg8b / cmpxchg16bxadd,它们通常仅用于同步/原子操作,而不是制作单线程代码 运行 更快。当然,像 add [mem], reg 这样的内存目标指令 lock add [mem], reg 情况之外很有用。)