Linux/SMP自旋锁是否速度过慢?

6

我一直在阅读理解Linux内核(Bovet&Cesati),其中关于内核同步的章节指出自旋锁获取代码可以简化为:

1: lock:
   btsl    $0, slp
   jnc     3
2: testb   $1, slp
   jne     2
   jmp     1
3: 

我最初认为,嵌套循环似乎是浪费的,你可以实现类似这样的内容:

1: lock:
   btsl    $0, slp
   jc      1

这将会简单很多。然而,我理解他们这么做的原因是因为lock会影响其他CPU,而btsl的时间比简单的testb要长。

唯一让我困惑的是随后的自旋锁释放。书中指出它产生了以下结果:

   lock:
   btrl    $0, slp

我的问题基本上是为什么?在我看来,一个lock/mov-immediate组合会更快。
您不需要将旧状态设置到进位标志中,因为按照内核无缺陷的规则(在内核的许多其他地方都假定了这一点),旧状态将为1(如果您尚未获得它,您就不会尝试释放它)。
而且,在386上,mov比btrl要快得多。
那么我错过了什么?
这些指令的时间是否在以后的芯片上发生了变化?
自该书印刷以来内核是否已更新?
该书是否只是纯粹错误(或显示简化指令)?
我是否错过了某些涉及CPU之间同步的其他方面,而更快的指令无法满足?
1个回答

10

“Understanding the Linux Kernel”这本书已经有些过时了。自从它的出版,Linux内核已经更新为使用所谓的Ticket Spinlocks。该锁基本上是一个16位的数量分成两个字节:我们可以称之为一个“Next”(如售货机中的下一个票)和另一个“Owner”(如柜台上显示的“现在服务”的号码)。Spinlock初始化时,两部分都设置为零。在加锁时,它会记录Spinlock的值并原子地递增Next。如果递增前的Next值等于Owner,则获得了该锁。否则,它会旋转,直到Owner被递增到正确的值为止。

相关代码在asm/spinlock.h中(适用于x86架构)。解锁操作确实比该书所述的要快得多且更简单:

static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)
{
    asm volatile(UNLOCK_LOCK_PREFIX "incb %0"
         : "+m" (lock->slock)
         :
         : "memory", "cc");
}

因为incbtr快大约8到9倍。

希望这可以帮助你;如果不行,我很乐意深入研究。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接