Linuxのスピンロックとは何ですか?


32

Linuxスピンロックについて詳しく知りたい。誰かが私にそれらを説明できますか?

回答:


34

スピンロックは、共有リソースが2つ以上のプロセスによって同時に変更されるのを防ぐ方法です。リソースを変更しようとする最初のプロセスは、ロックを「取得」し、その方法で続行し、リソースで必要なことを行います。その後ロックを取得しようとする他のプロセスは停止します。それらは、ロックが最初のプロセスによって解放されるのを待機している「スピンインプレース」と言われています。

Linuxカーネルは、特定の周辺機器にデータを送信するときなど、多くのことにスピンロックを使用します。ほとんどのハードウェア周辺機器は、複数の状態の同時更新を処理するようには設計されていません。2つの異なる変更を行う必要がある場合、1つは厳密にもう1つに従う必要があり、重複することはできません。スピンロックは必要な保護を提供し、変更が一度に1つずつ行われるようにします。

スピンロックは、スレッドのCPUコアが他の作業を行うことをブロックするため、問題です。Linuxカーネルは、その下で実行されるユーザー空間プログラムにマルチタスクサービスを提供しますが、その汎用マルチタスク機能はカーネルコードに拡張されません。

この状況は変化しており、Linuxのほとんどの存在のためです。Linux 2.0まで、カーネルはほぼ純粋にシングルタスクプログラムでした。CPUがカーネルコードを実行しているときは常に、1つのCPUコアのみが使用されました。これは、Big Kernel Lock(BKL )。Linux 2.2以降、BKLはゆっくりと多くの独立したロックに分割され、それぞれがより集中したクラスのリソースを保護します。現在、カーネル2.6にはBKLがまだ存在していますが、BKLは、よりきめ細かいロックに簡単に移動できない本当に古いコードでのみ使用されています。マルチコアボックスでは、すべてのCPUが有用なカーネルコードを実行できるようになりました。

Linuxカーネルには一般的なマルチタスク機能がないため、BKLを分割するユーティリティには制限があります。カーネルスピンロックでスピンしているCPUコアがブロックされると、ロックが解除されるまで他のことをするために再タスクすることはできません。ロックが解除されるまで、ただ座って回転します。

すべてのコアが常に単一のスピンロックを待機しているようなワークロードの場合、スピンロックはモンスターの16コアボックスを効果的にシングルコアボックスに変えることができます。これは、Linuxカーネルのスケーラビリティの主な制限です。CPUコアを2から4に倍増すると、おそらくLinuxボックスの速度がほぼ2倍になりますが、ほとんどのワークロードでは、16から32に倍速にならないでしょう。


@Warren:いくつかの疑問-このBig Kernel Lockとその意味についてもっと知りたいです。私はまた、最後の段落が理解DINT 「おそらく2〜4のCPUコアを2倍にすると、ほとんどのLinuxボックスの速度を倍にしますが、16から32に倍増することはおそらくないだろう」
セン

2
Re:BKLの意味:上記で明確にしたと思います。カーネルにロックが1つしかないため、2つのコアがBKLで保護されていることを試みると、1つのコアがブロックされ、最初の保護対象リソースの使用が終了します。ロックの粒度が細かいほど、これが発生する可能性が低くなるため、プロセッサの使用率が高くなります。
ウォーレンヤング

2
Re:doubling:プロセッサコアをコンピューターに追加すると、収益が減少するという法則があるということです。コアの数が増えると、コアの2つ以上が特定のロックで保護されたリソースにアクセスする必要が生じる可能性も高くなります。ロックの粒度を上げると、このような衝突の可能性が減りますが、あまりにも多くを追加することでオーバーヘッドが生じます。これは、最近何千ものプロセッサを搭載していることが多いスーパーコンピューターで簡単に確認できます。ほとんどのワークロードは、共有リソースの競合により多くのプロセッサーがアイドル状態になることを避けられないため、非効率です。
ウォーレンヤング

1
これは興味深い説明ですが(そのために+1)、スピンロックと他の種類のロックの違いを伝えるのに効果的ではないと思います。
ジル 'SO-悪であるのをやめる'

2
スピンロックとセマフォの違いを知りたい場合は、別の質問です。もう1つの良い質問ですが、相互排他的な質問です。ユーザーランドコードで一般的なミューテックスのようなものではなく、スピンロックを適切な選択肢にするLinuxカーネルの設計についてです。この答えは現状のままで十分に広まっています。
ウォーレンヤング

11

スピンロックとは、プロセスがロックを解除するために継続的にポーリングすることです。プロセスが(通常)不必要にサイクルを消費しているため、悪いと考えられます。Linux固有ではなく、一般的なプログラミングパターンです。そして、それは一般に悪い習慣と見なされますが、実際には正しい解決策です。スケジューラを使用するコストが、スピンロックの持続が予想される数サイクルのコストよりも高い場合があります(CPUサイクルの観点から)。

スピンロックの例:

#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
  sleep 1
done
#do stuff

多くの場合、スピンロックを回避する方法があります。この特定の例には、inotifywaitと呼ばれるLinuxツールがあります(通常、デフォルトではインストールされません)。Cで作成されている場合は、Linuxが提供するinotify APIを使用するだけです。

inotifywaitを使用した同じ例は、スピンロックなしで同じことを実現する方法を示しています。

#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff

そこでのスケジューラの役割は何ですか?それとも何か役割を持っていますか?
セン

1
スピンロックメソッドでは、スケジューラはタスクを実行するために1秒ごとにプロセスを再開します(これは単にファイルの存在を確認するだけです)。inotifywaitの例では、スケジューラは子プロセス(inotifywait)が終了したときにのみプロセスを再開します。Inotifywaitもスリープしています。スケジューラは、inotifyイベントが発生したときにのみ再開します。
ショーンJ.ゴフ

それでは、このシナリオはシングルコアプロセッサシステムでどのように処理されますか?
セン

@Sen:これはLinux Device Driversでかなりよく説明されています。
ジル 'SO-悪であるのをやめる'

1
そのbashスクリプトは、スピンロックの悪い例です。プロセスを一時停止して、スリープ状態にします。スピンロックは決して眠りません。シングルコアマシンでは、スケジューラを一時停止して続行します(ビジーな待機なし)
マーティン

7

スレッドがロックを取得しようとすると、失敗すると次の3つのことが起こります。試行とブロック、試行と続行、スリープへ移行して、イベントが発生したときにOSにウェイクアップするように伝えます。

これで、試行と継続は、試行とブロックよりもはるかに少ない時間で済みます。しばらくの間、「試行して続行」にはi単位の時間がかかり、「試行してブロック」には100時間がかかるとします。

ここで、スレッドが平均してロックを保持するのに4単位の時間がかかると仮定します。100単位を待つのはもったいない。そのため、代わりに「try and continue」のループを作成します。4回目の試行では、通常ロックを取得します。これはスピンロックです。これは、スレッドがロックを取得するまで所定の位置で回転し続けるためです。

追加の安全対策は、ループの実行回数を制限することです。したがって、この例ではforループをたとえば6回実行し、失敗した場合は「試行してブロック」します。

スレッドが常に約200ユニットのロックを保持することがわかっている場合、試行と継続のたびにコンピューターの時間を無駄にしています。

そのため、最終的に、スピンロックは非常に効率的または無駄になります。ロックを保持する「典型的な」時間が「試行してブロック」するのにかかる時間よりも長い場合は無駄です。通常、ロックを保持する時間が「試行してブロックする」時間よりもはるかに短い場合に効率的です。

追伸:スレッドについて読む本は、まだ見つけられるなら「スレッド入門書」です。


スレッドはロックを取得するまで所定の位置で回転し続けます。この紡績は何ですか?wait_queueに来て、スケジューラによって実行されるようになっていますか?たぶん私は低レベルになろうとしていますが、それでも私の疑問を抱くことができません。
セン

基本的に、ループ内の3〜4個の命令間でスピンします。
ポールステリアン

5

ロックが同期するように二つ以上のタスク(プロセス、スレッド)のための方法です。具体的には、両方のタスクが一度に1つのタスクのみが使用できるリソースに断続的にアクセスする必要がある場合、タスクがリソースを同時に使用しないように調整する方法です。リソースにアクセスするには、タスクは次の手順を実行する必要があります。

take the lock
use the resource
release the lock

別のタスクがすでにロックを取得している場合、ロックを取得することはできません。(ロックを物理的なトークンオブジェクトと考えてください。オブジェクトが引き出しにあるか、誰かが手に持っています。オブジェクトを保持している人だけがリソースにアクセスできます。)他の誰もロックしていないので、それを取得します」。

高レベルの観点から見ると、ロックを実装するには、スピンロックと条件の2つの主要な方法があります。スピンロック誰もまでちょうど「スピニング」(つまり、ループで何もしない)ロック手段を取って、他のロックを持っています。条件により、タスクがロックを取得しようとしたが、別のタスクがロックを保持しているためにブロックされた場合、新人は待機キューに入ります。リリース操作は、ロックが使用可能になったことを待機タスクに通知します。

(これらの説明は、ロックを実装するのに十分ではありません。原子性については何も述べていないためです。しかし、原子性はここでは重要ではありません。)

スピンロックは明らかに無駄です。待機中のタスクはロックがかかっているかどうかをチェックし続けます。それで、なぜ、そして、いつ、それは使われますか?スピンロックは、ロックが保持されていない場合に入手するのが非常に安価なことがよくあります。これにより、ロックが保持される機会が少ない場合に魅力的です。さらに、スピンロックは、ロックの取得に時間がかかると予想されない場合にのみ実行可能です。そのため、スピンロックは非常に短い時間保持される状況で使用される傾向があるため、ほとんどの試行は最初の試行で成功し、待機が必要な試行は長く待機しません。

Linuxデバイスドライバーの第5章には、Linuxカーネルのスピンロックおよびその他の同時実行メカニズムに関する適切な説明があります。


他の同期プリミティブを実装する良い方法は何でしょうか?スピンロックを取得し、他の誰かが実装されている実際のプリミティブを持っているかどうかを確認してから、スケジューラを使用するか、アクセスを許可しますか?Javaのsynchronized()ブロックをスピンロックの一種と考え、適切に実装されたプリミティブではスピンロックを使用するだけですか?
ポールステリアン

@PaulStelianこの方法で「低速」ロックを実装することは確かに一般的です。私はその部分に答えるのに十分なJavaを知りませんが、それがsynchronizedスピンロックによって実装されることを疑います:synchronizedブロックは非常に長い間実行できます。synchronizedは、特定の場合にロックを使いやすくするための言語構成体であり、より大きな同期プリミティブを構築するためのプリミティブではありません。
ジル「SO-悪であるのをやめなさい」

3

スピンロックは、スケジューラを無効にすることで動作するロックであり、ロックが取得される特定のコアで割り込み(irqsaveバリアント)を行う可能性があります。ミューテックスとは異なり、スピンロックが保持されている間はスレッドのみが実行できるようにスケジューリングを無効にします。ミューテックスを使用すると、他の優先度の高いスレッドを保留中にスケジュールできますが、保護されたセクションを同時に実行することはできません。スピンロックはマルチタスクを無効にするため、スピンロックを取得してから、ミューテックスを取得しようとする他のコードを呼び出すことはできません。スピンロックセクション内のコードは決してスリープしてはいけません(通常、ロックされたミューテックスまたは空のセマフォに遭遇すると、コードはスリープします)。

ミューテックスとのもう1つの違いは、スレッドは通常、ミューテックスのキューにあるため、その下のミューテックスにはキューがあることです。一方、spinlockは、必要な場合でも他のスレッドが実行されないようにします。したがって、ファイルの外部でスリープしないかどうかわからない関数を呼び出すときは、スピンロックを保持してはいけません。

スピンロックを割り込みと共有する場合、irqsaveバリアントを使用する必要があります。これは、スケジューラを無効にするだけでなく、割り込みも無効にします。それは理にかなっていますか?Spinlockは、他に何も実行されないようにすることで機能します。割り込みを実行したくない場合は、割り込みを無効にし、クリティカルセクションに安全に進みます。

マルチコアマシンでは、スピンロックは実際にスピンし、ロックを保持している別のコアがロックを解除するのを待ちます。このスピンはマルチコアマシンでのみ発生します。シングルコアマシンでは発生しないためです(スピンロックを保持して続行するか、ロックが解除されるまで実行しないかのいずれかです)。

スピンロックは、理にかなっている場合は無駄ではありません。非常に小さなクリティカルセクションの場合、重要な作業を完了するために数マイクロ秒間スケジューラーを一時停止するのに比べて、mutexタスクキューを割り当てることは無駄です。ioオペレーション(スリープ状態になる可能性がある)でスリープまたはロックを保持する必要がある場合は、ミューテックスを使用します。確かにスピンロックを決してロックせず、割り込み内でそれを解放しようとします。これは機能しますが、while(flagnotset)のarduino crapのようになります。そのような場合は、セマフォを使用します。

メモリトランザクションのブロックの単純な相互排除が必要な場合は、スピンロックを取得します。ミューテックスロックの直前に複数のスレッドを停止し、ミューテックスが解放され、同じスレッドでロックおよびリリースするときに優先順位が最も高いスレッドを選択する場合は、ミューテックスを取得します。あるスレッドまたは割り込みでポストし、別のスレッドで取得する場合は、セマフォを取得します。相互排除を保証するためのわずかに異なる3つの方法であり、わずかに異なる目的に使用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.