3

我阅读了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.如何监控spinlock的等待时间(第一次尝试锁,最后拿到锁的时间)?变量inc是指spinlock的等待时间吗?

4

1 回答 1

2

让我先解释一下自旋锁代码是如何工作的。我们有变量

uint16_t inc = 0x0100,
         lock->slock;     // I'll just call this "slock"

在汇编代码中,被inc称为%0和。此外,表示低 8 位,即,并且是。slock%1%b0inc % 0x100%h0inc / 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

我没有测试过这个,但这是一般的想法。

于 2013-07-01T13:30:01.497 回答