我已经在Windows的NASM-64中实现了自旋锁。我正在使用自旋锁来阻止共享内存缓冲区,以便每个内核一次将一个内核依次写入共享缓冲区(内核0优先,内核1秒,内核2第三等)。 ,则互斥或信号量将不允许我按核心顺序进行缓冲区写操作,而自旋锁则更可取,因为它不使用OS调用。
这是有争议的代码部分。这不是一个完整的示例,因为问题出在更大的汇编程序的这一小部分。我将cmpxchg用于原子性。
在进入本节时,rax包含内核号,而在rbx处包含一个称为spinlock_core的存储变量,在进入本节时将其设置为零(第一个内核)。每个内核完成后,spinlock_core递增到下一个内核编号。
mov rdi,Test_Array
movq rbx,xmm11
mov [rdi+rax],rbx ; rax contains the core number offset (0, 8, 16, 24)
push rax
; Spin Lock
spin_lock_01:
lock cmpxchg [spinlock_core],rax ; spinlock_core is set to zero on first entry
jnz spin_lock_01
; To test the result:
mov rdi,Test_Array
mov [rdi+rax+32],rax
jmp out_of_here
该测试的结果在Test_Array中,该数组中填充了每个内核要写入的字节数。返回时包含:
40, 40, 40, 16, 0, 8, 16, 24
显示内核0-2每个都有40字节要写入,内核3有16个字节要写入。但是,Test_Array的最后四个元素包含四个核心中每个核心的偏移量。如果自旋锁工作正常(cmpxchg rax,rbx),则后四个元素应全部包含零,这表明只允许第一个内核通过。但是它表明所有四个核心都被允许通过。
我假设我的cmpxchg不是原子的,这就是其他内核泄漏的原因-仅当spinlock_core递增到下一个内核时才允许它们进入,但在我们使用jmp out_of_here退出之前不会发生。根据文档,cmpxchg应该在前缀“ lock”之前,例如在cmpxchg rax,rbx锁中,但是当我这样做时,NASM汇编器会显示“警告:指令不可锁定[-w + lock]”。 Felix Cloutier的站点说,只有在涉及到内存操作数时才需要使用锁前缀,但是当我编写“ cmpxchg rax,[spinlock_core]”时,我会得到“操作码和操作数的无效组合”。
总而言之,我的问题是:为什么cmpxchg不是上面写的原子,为什么NASM汇编器不允许使用锁前缀?
关于原子溢出问题,有很多关于Stack Overflow和其他地方的详细文章,但是我还没有找到解决这个特定问题的文章。
感谢您的帮助。
答案 0 :(得分:0)
在下面的@Peter Cordes和@prl的评论的帮助下,这就是我解决此问题的方法。所做的更改是(1)使用bx,而不将ax用作目标寄存器,以及(2)仅使用低8位寄存器ax和bx,而不使用rax和rbx。
xor rbx,rbx
spin_lock_01:
pop rax ; the core number is stored in the stack
mov bl,al ; put core number into bx
mov rcx,rax ; preserve core number for later use
push rax ; push core number back to stack
lock cmpxchg [spinlock_core],bl ; spinlock_core is set to zero on first entry
jnz spin_lock_01
; now test it
mov rdi,Test_Array
mov rbx,[spinlock_core]
mov rdx,[order_of_execution]
add rdx,1
mov [order_of_execution],rdx
mov [rdi+rcx+0],rcx
mov [rdi+rcx+32],rdx
add rbx,8
mov [spinlock_core],rbx
jmp out_of_here
Test_Array中的输出显示为:0,8,16,24,1,2,3,4。这表明每个核心都按核心顺序(1,2,3,4)传递。每个内核完成时,内存位置spinlock_core会增加,以信号通知下一个内核。