我阅读了英特尔手册,发现指令有一个锁定前缀,可以防止处理器同时写入同一个内存位置。我很兴奋。我想它可以用作硬件互斥。所以我写了一段代码来拍摄。结果非常令人沮丧。锁不支持MOV或LEA指令。手册说LOCK仅支持ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCH8B,DEC,INC,NEG,NOT,OR,SBB,SUB,XOR,XADD和XCHG。更重要的是,如果LOCK前缀与这些指令之一一起使用且源操作数是内存操作数,则可能会生成未定义的操作码异常(#UD)。
我想知道为什么这么多限制,如此多的限制使得LOCK看起来毫无用处。我不能用它来保证一般的写操作没有脏数据或并行引起的其他问题。
E.g。我在C中编写了代码++(* p).p是指向共享内存的指针。相应的程序集如下:
movl 28(%esp), %eax
movl (%eax), %eax
leal 1(%eax), %edx
movl 28(%esp), %eax
movl %edx, (%eax)
我在“movl”和“leal”之前添加了“lock”,但是处理器抱怨“无效指令”。 :-(我想将序列化写操作的唯一方法是使用软件互斥,对吧?
答案 0 :(得分:12)
我当然不会称lock
无用。 lock cmpxchg
是执行compare-and-swap的标准方法,fetch-and-add是许多同步算法的基本构建块。
另请参阅{{3}}。
答案 1 :(得分:5)
lock
的目的是使操作原子,而非序列化。这样,在操作生效之前,CPU不能被抢占。
答案 2 :(得分:2)
x86处理器以毛茸茸的设计着称,具有许多功能,许多规则,甚至更多例外。这与家庭的悠久历史有关。
当编译器或人员使用LOCK
时,他们总是使用它的所有限制,通常是专门为在线程之间执行同步而引入的数据,而不是算法最终操作的应用程序数据。然后,人们将线程同步协议调整为LOCK
可以为它们做什么,而不是相反。
您似乎寻找的一般类型的指令称为memory barriers。实际上,x86有来自这个家族的几个“现代”指令(MFENCE,LFENCE,SFENCE)。它们分别是完整的围栏,负载围栏和商店围栏。但是,它们在指令集中的重要性仅限于SSE,因为英特尔保证在指令集的传统部分上进行写入的序列化,这就是为什么这个老化的体系结构是多线程的一个简单目标的原因编程。
有关详细信息,另请参阅this answer。
答案 3 :(得分:1)
在您提供的示例中,您可以使用lock
前缀和inc
这样的说明(假设p
位于%eax
):
lock inc (%eax)
在更一般的情况下,你必须使用锁。
答案 4 :(得分:0)
在多处理器计算机上,有两个并发进程使用相同的数据但无法同时修改它时,这很有用。
当其中一个进程正在修改数据时,它会对修改指令使用锁定,这样,当第二个进程尝试修改它时,它必须等待第一个进程完成其工作才能执行轮流拥有。
我希望这会有所帮助。