自己动手写自旋锁

自旋锁以其高效闻名。顾名思义,自旋即如果无法成功锁住锁资源则会一直循环尝试锁,这与互斥锁的行为较为不同(互斥锁如果无法锁住则会挂起等待)。但其特性也决定了其使用场景,对于简单几步即可完成对共享资源操作的场景,自旋锁效率将会高于互斥量。

本文给出自旋锁的一种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处理器引入的。


上面的例子中,我们还使用了`volatile`关键字,这个关键字在网上有非常多的文章详细解释,主要是两方面作用:
  • 告诉编译器该关键字部分不可以做任何优化
  • 使用该关键字的变量每次必须从内存中读取,而不能放在cache或寄存器中备份

感谢阅读!

你可能感兴趣的:(c语言,linux,经验分享,程序人生,学习,服务器,后端)