6

カーネル同期の章を読んUnderstanding the Linux kernel (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に影響を与え、単純なCPUよりもタイミングbtslが大きいため、なぜそれを行ったのかがわかりtestbます。

頭を動かすことができなかったのは、その後のスピンロックのリリースです。この本は、それが次のことをもたらすと述べています:

   lock:
   btrl    $0, slp

私の質問は基本的になぜですか?lock/mov-immediateコンボの方が速いようです。

カーネルにはバグがないという規則に従って(カーネル内の他の多くの場所で想定されている)、古い状態は1になるため、古い状態をキャリーフラグにする必要はありません(そうはなりません)。まだ取得していない場合は、リリースしようとしています)。

そして、少なくとも386では、movaはaよりもはるかに高速です。btrl

だから私は何が欠けていますか?

後のチップでそれらの命令のタイミングを変更しましたか?

本が印刷されてからカーネルは更新されましたか?

その本はまったく間違っている(または簡単な説明を示している)のですか?

より高速な命令では満たされないCPU間の同期に関する他の側面を見逃したことがありますか?

4

1 に答える 1

10

さて、Understanding the Linux Kernel古いです。それが書かれて以来、Linuxカーネルはいわゆるチケットスピンロックを使用するように更新されました。ロックは基本的に2バイトに分割された16ビットの数量です。1つNext(ディスペンサーの次のチケットのように)ともう1つ(Ownerカウンターの上の「NowServing」番号のように)を呼び出しましょう。スピンロックは、両方の部分がゼロに設定された状態で初期化されます。Lockingは、スピンロックの値を記録し、Next、atomicをインクリメントします。インクリメントする前の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");
}

は。incよりも約8倍または9倍速いからですbtr

お役に立てれば; そうでなければ、私はもっと深く掘り下げたいと思います。

于 2011-01-19T15:41:04.390 に答える