自旋锁以其高效闻名。顾名思义,自旋即如果无法成功锁住锁资源则会一直循环尝试锁,这与互斥锁的行为较为不同(互斥锁如果无法锁住则会挂起等待)。但其特性也决定了其使用场景,对于简单几步即可完成对共享资源操作的场景,自旋锁效率将会高于互斥量。
本文给出自旋锁的一种x86平台实现。
其实,gcc在4.1以后的版本就支持了内置的自旋锁实现,而各大类UNIX系统中也提供了pthread_spin_lock这样的自旋锁函数。那么为什么还要自己实现呢?
这是由于一些历史遗留问题和跨平台移植共同导致的结果,例如RHEL5 32位系统,其自带的gcc并不支持自旋锁,也没有pthread_spin_lock,但我们依旧需要使用自旋锁特性,那么只好自己动手丰衣足食了。
闲话少叙,代码奉上:
#define barrier() asm volatile ("": : :"memory")
#define cpu_relax() asm volatile ("pause\n": : :"memory")
static inline unsigned long xchg(void *ptr, unsigned long x)
{
__asm__ __volatile__("xchg %0, %1"
:"=r" (x)
:"m" (*(volatile long *)ptr), "0"(x)
:"memory");
return x;
}
void spin_lock(void *lock)
{
unsigned long *l = (unsigned long *)lock;
while (1) {
if (!xchg(l, 1)) return;
while (*l) cpu_relax();
}
}
void spin_unlock(void *lock)
{
unsigned long *l = (unsigned long *)lock;
barrier();
*l = 0;
}
int spin_trylock(void *lock)
{
return !xchg(lock, 1);
}
可以看到,这里我们使用了内联汇编,且用到了汇编指令pause
。
关于pause
,给出pause
指令的一段解释:
Improves the performance of spin-wait loops. When executing a
“spin-wait loop,” a Pentium 4 or Intel Xeon processor suffers a severe
performance penalty when exiting the loop because it detects a
possible memory order violation. The PAUSE instruction provides a hint
to the processor that the code sequence is a spin-wait loop. The
processor uses this hint to avoid the memory order violation in most
situations, which greatly improves processor performance. For this
reason, it is recommended that a PAUSE instruction be placed in all
spin-wait loops.
大意是说这个指令可以提升自旋等待的性能,这个指令将作为对处理器的一个线索或者暗示(hint
),让处理器尽量避免该指令出现时可能存在的内存乱序存储问题。
同时这个指令也将降低自旋等待时的电力消耗。pause
指令是Pentium 4处理器引入的。