我在linux内核中读取了spinlock函数代码。 有两个与自旋锁相关的function。 请参阅下面的代码:
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) { short inc = 0x0100; asm volatile ( LOCK_PREFIX "xaddw %w0, %1\n" "1:\t" "cmpb %h0, %b0\n\t" "je 2f\n\t" "rep ; nop\n\t" "movb %1, %b0\n\t" /* don't need lfence here, because loads are in-order */ "jmp 1b\n" "2:" : "+Q" (inc), "+m" (lock->slock) : : "memory", "cc"); } static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) { int inc = 0x00010000; int tmp; asm volatile(LOCK_PREFIX "xaddl %0, %1\n" "movzwl %w0, %2\n\t" "shrl $16, %0\n\t" "1:\t" "cmpl %0, %2\n\t" "je 2f\n\t" "rep ; nop\n\t" "movzwl %1, %2\n\t" /* don't need lfence here, because loads are in-order */ "jmp 1b\n" "2:" : "+r" (inc), "+m" (lock->slock), "=&r" (tmp) : : "memory", "cc"); }
我有两个问题:
1.上面两个函数有什么区别?
2.我能做些什么来监控自旋锁的等待时间(第一次尝试锁的时间并最终获得锁)?variablesinc是指自旋锁的等待时间吗?
让我先解释一下spinlock代码是如何工作的。 我们有变数
uint16_t inc = 0x0100, lock->slock; // I'll just call this "slock"
在汇编程序代码中, inc
被称为%0
, slock
称为%1
。 而且, %b0
表示低8位,即inc % 0x100
, %h0
是inc / 0x100
。
现在:
lock xaddw %w0, %1 ;; "inc := slock" and "slock := inc + slock" ;; simultaneously (atomic exchange and increment) 1: cmpb %h0, %b0 ;; "if (inc / 256 == inc % 256)" je 2f ;; " goto 2;" rep ; nop ;; "yield();" movb %1, %b0 ;; "inc = slock;" jmp 1b ;; "goto 1;" 2:
如果inc
为零,则比较inc
的高位字节和低位字节。 由于inc
具有原始锁的值,所以如果锁被解锁,则会发生这种情况。 在这种情况下,锁已经被原子交换和增量增加到非零,所以现在它被锁定了。
否则,即,如果锁已经被锁定,我们暂停一下,然后更新inc
到当前的锁的值,然后再试一次。
(我相信实际上有一个溢出的可能性,如果2 8个线程同时试图获得自旋锁,在这种情况下, slock
更新为0x0100,0x0200,… 0xFF00,0x0000,然后似乎是解锁。这就是为什么代码的第二个版本使用一个16位宽的计数器,这将需要2 16个同时尝试。)
现在让我们插入一个计数器:
uint32_t spincounter = 0; asm volatile( /* code below */ : "+Q" (inc), "+m" (lock->slock) : "=r" (spincounter) : "memory", "cc");
现在spincounter
可能被称为%2
。 我们只需要每次递增计数器:
1: inc %2 cmpb %h0, %b0 ;; etc etc
我没有测试过这个,但是这是一般的想法。