我看了linux内核中的自旋锁函数代码。与自旋锁相关的函数有两个。请看下面的代码:
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.如何监控自旋锁等待时间(从第一次尝试锁到最后拿到锁的时间)?变量inc是指自旋锁等待时间吗?
最佳答案
首先让我解释一下自旋锁代码是如何工作的。我们有变数
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
更新为锁的当前值,然后重试。
(我相信实际上有可能发生溢出,如果 28 个线程同时尝试获取自旋锁。在这种情况下,slock
将更新为 0x0100、0x0200 , ... 0xFF00, 0x0000, 然后似乎被解锁了。也许这就是代码的第二个版本使用 16 位宽计数器的原因,这将需要 216 同时尝试。)
现在让我们插入一个计数器:
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
我还没有测试过这个,但这是一般的想法。
关于c - 如何监控linux自旋锁等待时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17400261/