x86自旋锁使用cmpxchg
我是使用gcc内联汇编的新手,并且想知道在x86多核机器上是否可以实现自旋锁(无竞态条件)(使用AT&T语法):
spin_lock: mov 0 eax lock cmpxchg 1 [lock_addr] jnz spin_lock ret spin_unlock: lock mov 0 [lock_addr] ret
你有正确的想法,但你的创业板已经破裂:
cmpxchg
不能使用立即数操作数,只能注册。
lock
不是mov
的有效前缀。 mov
到一个对齐的地址在x86上是原子的,所以你不需要lock
。
我使用AT&T语法已经有一段时间了,希望我能记住所有的东西:
spin_lock:
xorl %ecx, %ecx
incl %ecx # newVal = 1
spin_lock_retry:
xorl %eax, %eax # expected = 0
lock; cmpxchgl %ecx, (lock_addr)
jnz spin_lock_retry
ret
spin_unlock:
movl $0, (lock_addr) # atomic release-store
ret
请注意,GCC具有原子构建,因此您实际上不需要使用内联asm来完成此操作:
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1));
}
void spin_unlock(int volatile *p)
{
asm volatile ("":::"memory"); // acts as a memory barrier.
*p = 0;
}
正如Bo在下面所说的,锁定指令会产生一定的成本:您使用的每个锁定指令必须获得对cache行的独占访问权,并在lock cmpxchg
运行时将其lock cmpxchg
,这可以延迟解锁线程,特别是在多个线程正在等待锁定的情况下。 即使没有很多CPU,它仍然很容易,并且值得优化:
void spin_lock(int volatile *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1))
{
// spin read-only until a cmpxchg might succeed
while(*p) _mm_pause(); // or maybe do{}while(*p) to pause first
}
}
pause
指令对于超线程CPU的性能至关重要,因为当你有这样的代码旋转时 - 它允许第二个线程在第一个线程旋转时执行。 在不支持pause
CPU上,它被视为nop
。
当离开旋转循环时, pause
还可以防止记忆顺序错误猜测,当它终于到了再次做实际工作的时候时。
请注意,真正的自旋锁实现不会永远旋转; 他们回到操作系统辅助的睡眠和通知机制。 他们也可能采取措施来提高公平性,以及cmpxchg
/ pause
循环无法做的其他许多事情。
这会减少内存总线上的争用:
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1)) while(*p);
}
链接地址: http://www.djcxy.com/p/64601.html
上一篇: x86 spinlock using cmpxchg
下一篇: PeekMessage() throws an unhandled exception (access violation)