7

我阅读了英特尔手册,发现指令有一个锁定前缀,可以防止处理器同时写入相同的内存位置。我对此感到非常兴奋。我想它可以用作硬件互斥锁。所以我写了一段代码来试一试。结果相当令人沮丧。该锁不支持 MOV 或 LEA 指令。手册上说 LOCK 只支持 ADD、ADC、AND、BTC、BTR、BTS、CMPXCHG、CMPXCH8B、DEC、INC、NEG、NOT、OR、SBB、SUB、XOR、XADD 和 XCHG。更重要的是,如果 LOCK 前缀与这些指令之一一起使用并且源操作数是内存操作数,则可能会生成未定义的操作码异常 (#UD)。

我想知道为什么这么多限制,这么多限制让 LOCK 看起来毫无用处。我不能用它来保证一般的写操作没有脏数据或其他由并行引起的问题。

例如,我在 C 中编写了代码 ++(*p)。p 是指向共享内存的指针。对应的程序集如下:

movl    28(%esp), %eax
movl    (%eax), %eax
leal    1(%eax), %edx
movl    28(%esp), %eax
movl    %edx, (%eax)

我在“movl”和“leal”之前添加了“lock”,但处理器抱怨“Invalid Instruction”。:-( 我想使写操作序列化的唯一方法是使用软件互斥锁,对吧?

4

5 回答 5

12

我当然不会说lock没用。lock cmpxchg是执行比较和交换的标准方法,它是许多同步算法的基本构建块。

另请参阅fetch-and-add

于 2012-06-16T17:45:24.467 回答
5

的目的lock是使操作原子化,而不是序列化。这样CPU在操作生效之前就不能被抢占。

于 2012-06-16T17:45:47.710 回答
3

x86 处理器以其毛茸茸的设计而闻名,它具有许多特性、许多规则,甚至还有所有这些规则的更多例外。这与家族的悠久历史有关。

当编译器或人们在使用LOCK时,他们总是在使用它时受到所有限制,通常是在专门引入以执行线程之间同步的数据上,而不是算法最终操作的应用程序数据。然后将线程同步协议调整为LOCK可以为它们做什么,而不是反之。

您似乎要寻找的一般指令类型称为内存屏障。事实上,x86 有几个来自这个系列的“现代”指令(MFENCE、LFENCE、SFENCE)。它们分别是全围栏、装载围栏和存储围栏。然而,它们在指令集中的重要性仅限于SSE,因为英特尔保证对指令集传统部分的写入序列化,这就是为什么这种陈旧的架构很容易成为多线程编程目标的原因。

另请参阅此答案以获取更多信息。

于 2012-06-17T07:28:42.577 回答
2

当在多处理器机器上,有两个并发进程正在使用相同的数据但它们不能同时修改它时,它很有用。

当其中一个进程正在修改数据时,它会在修改指令上使用锁定,这样,当第二个进程尝试修改它时,它必须等待第一个进程完成其工作,然后才能自行完成。转动。

我希望这会有所帮助。

于 2012-06-16T18:46:21.473 回答
1

在您提供的示例中,您可以将lock前缀与这样的inc指令一起使用(假设p位于 中%eax):

lock inc (%eax)

在更一般的情况下,您必须使用锁。

于 2015-05-26T14:13:36.333 回答