【问题标题】:How to monitor linux spinlock waiting time?如何监控linux自旋锁等待时间?
【发布时间】:2013-07-01 08:34:12
【问题描述】:

我阅读了linux内核中的spinlock函数代码。有两个与自旋锁相关的函数。请看下面的代码:

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是指自旋锁的等待时间吗?

【问题讨论】:

  • 1) 不多。看起来他们只是使用不同的值来指示“锁定”状态。 2)您可以在代码中添加一个计数器。就目前而言,没有任何东西可以计算重复次数。但是,您也可以用rdtsc 循环计数围绕您的锁定代码进行粗略测量。
  • @KerrekSB 谢谢。 “rep ; nop\n\t”这行是否意味着旋转忙等待?
  • 不 - 这只是一个“产量”。现代处理器有一个专用的pause 指令。旋转是jmp 1。 (也许看看this article。)
  • @KerrekSB,你能帮我在 asm 代码中添加一个无符号长计数器(监控旋转时间)吗,我不熟悉 asm 代码。非常感谢。

标签: c linux assembly linux-kernel spinlock


【解决方案1】:

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

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

在汇编代码中,inc 被称为%0slock 被称为%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

我没有对此进行测试,但这是一般的想法。

【讨论】:

  • 是的,第一个代码仅适用于
  • @Roland:谢谢!很高兴知道。 (我忘记了“线程”在内核空间中的概念不如“CPU”有用。)
  • @Kerrek SB。非常感谢。我在 linux 内核中尝试了你的代码,但它没有给出任何关于自旋锁等待时间信息的输出信息。你能告诉我哪里错了吗?我的电脑架构是x86_64,我的操作系统是linux-2.6.31.10。
  • @Charles0429:您记录了spincounter 变量的值吗?它说什么?
  • @KerrekSB。是的,我尝试将 spincounter 变量从虚拟机记录到 xen 管理程序,但它没有生成任何输出。我试图在包含ticket_spinlock的文件中写一个非法语句,令我惊讶的是,没有遇到错误,所以我想知道在linux-kernel 2.6.31.10中使用了ticket spinlock?虽然我用谷歌搜索的是“自 linux kenerl 2.6.25 以来 x86 架构使用票证自旋锁”。
猜你喜欢
  • 2013-03-22
  • 2016-11-02
  • 2022-08-19
  • 2014-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-23
相关资源
最近更新 更多