11

Linuxカーネルの循環バッファの実装について説明している記事がhttp://lwn.net/Articles/378262/にあります。いくつか質問があります:

これが「プロデューサー」です。

spin_lock(&producer_lock);

unsigned long head = buffer->head;
unsigned long tail = ACCESS_ONCE(buffer->tail);

if (CIRC_SPACE(head, tail, buffer->size) >= 1) {
    /* insert one item into the buffer */
    struct item *item = buffer[head];

    produce_item(item);

    smp_wmb(); /* commit the item before incrementing the head */

    buffer->head = (head + 1) & (buffer->size - 1);

    /* wake_up() will make sure that the head is committed before
     * waking anyone up */
    wake_up(consumer);
}

spin_unlock(&producer_lock);

質問:

  1. このコードはメモリオーダリングとアトミック性を明示的に扱っているので、spin_lock()のポイントは何ですか?
  2. これまでのところ、ACCESS_ONCEはコンパイラの並べ替えを停止するというのが私の理解ですが、本当ですか?
  3. produce_item(item)は、アイテムに関連付けられたすべての書き込みを発行するだけですか?
  4. smp_wmb()は、それに続く「公開」書き込みの前に、produce_item(item)へのすべての書き込みが完了することを保証すると思います。本当ですか?
  5. このコードを入手したページの解説は、ヘッドインデックスを更新した後、通常はsmp_wmb()が必要になることを示唆しているようですが、wake_up(consumer)はこれを行うため、必要ありません。本当?もしそうなら、なぜですか?

これが「消費者」です。

spin_lock(&consumer_lock);

unsigned long head = ACCESS_ONCE(buffer->head);
unsigned long tail = buffer->tail;

if (CIRC_CNT(head, tail, buffer->size) >= 1) {
    /* read index before reading contents at that index */
    smp_read_barrier_depends();

    /* extract one item from the buffer */
    struct item *item = buffer[tail];

    consume_item(item);

    smp_mb(); /* finish reading descriptor before incrementing tail */

    buffer->tail = (tail + 1) & (buffer->size - 1);
}

spin_unlock(&consumer_lock);

「消費者」に固有の質問:

  1. smp_read_barrier_depends()は何をしますか?フォーラムのいくつかのコメントから、ここでsmp_rmb()を発行できたようですが、一部のアーキテクチャでは、これは不要(x86)で高すぎるため、オプションでこれを行うためにsmp_read_barrier_depends()が作成されました...なぜsmp_rmb()が必要なのか本当に理解できません!
  2. smp_mb()は、それ以前のすべての読み取りが、その後の書き込みの前に完了することを保証するためにありますか?
4

1 に答える 1

8

プロデューサーの場合:

  1. これspin_lock()は、2つのプロデューサーが同時にキューを変更しようとするのを防ぐためです。
  2. ACCESS_ONCE並べ替えを防ぎますが、コンパイラが後で値を再ロードすることも防ぎます。(これをさらに拡張するLWNに関する記事があります)ACCESS_ONCE
  3. 正しい。
  4. また正しい。
  5. ここでの(暗黙の)書き込みバリアは、コンシューマーをウェイクアップする前に必要です。そうしないと、コンシューマーが更新されたhead値を見ることができない可能性があります。

消費者:

  1. smp_read_barrier_depends()はデータ依存性バリアであり、読み取りバリアの弱い形式です(2を参照)。この場合の効果はbuffer->tail、で配列インデックスとして使用する前に、が読み取られることを確認することですbuffer[tail]
  2. smp_mb()これが完全なメモリバリアであり、すべての読み取りと書き込みがこの時点までにコミットされるようにします。

その他の参考資料:

(注:プロデューサーで5、コンシューマーで1の答えについては完全にはわかりませんが、事実のかなりの概算だと思います。メモリバリアに関するドキュメントページを読むことを強くお勧めします。私がここに書くことができる何よりも包括的です。)

于 2013-01-17T17:39:37.220 に答える