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

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

Linuxスピンロックについてもっと知りたいです。誰かが私に説明できますか?

答え1

スピンロックは、複数のプロセスによる同時変更から共有リソースを保護する方法です。リソースを修正しようとする最初のプロセスは、ロックを「取得」し、続行し、リソースに対して必要なタスクを実行します。その後、ロックを取得しようとする他のすべてのプロセスは停止し、最初のプロセスがロックを解除するのを待つため、スピンロックと呼ばれます。

Linuxカーネルは、特定のペリフェラルにデータを送信するときなど、さまざまな方法でスピンロックを使用します。ほとんどのハードウェアペリフェラルは、複数の同時状態更新を処理するように設計されていません。 2つの異なる修正を適用する必要がある場合、1つは他のものに厳密に従う必要があり、重複することはできません。スピンロックは、一度に1つの修正のみを行うために必要な保護を提供します。

スピンロックは、スピンによってそのスレッドのCPUコアが他のタスクを実行するのを妨げるために問題になります。 Linuxカーネルは、その下で実行されるユーザースペースプログラムにマルチタスクサービスを提供しますが、この一般的なマルチタスク機能はカーネルコードには拡張されません。

これは変化しており、ほとんどのLinuxの存在でした。 Linux 2.0以前では、カーネルはほぼ純粋に単一の作業プログラムでした。 CPUがカーネルコードを実行するたびに、BKL(Big Kernel Lock)と呼ばれるすべての共有リソースを保護するスピンロックがあったため、1つのCPUコアのみが使用されました。 Linux 2.2以降、BKLは徐々に複数の独立したロックに分解され、各ロックはより集中したリソースクラスを保護します。今日、カーネル2.6にはまだBKLがありますが、より細かいロックに簡単に移動できない非常に古いコードでのみ使用されています。今日、マルチコアシステムでは、各CPUが有用なカーネルコードを実行する可能性が高いです。

Linuxカーネルには汎用マルチタスク機能がないため、BKL因数分解の有用性が制限されます。カーネルスピンロックでCPUコアの回転がブロックされると、ロックが解除されるまでタスクを再割り当てできず、他のタスクを実行できません。ロックが解除されるまで、ただ座って回転させます。

ワークロードが各コアが常に単一のスピンロックを待つ程度であれば、スピンロックは巨大な16コアボックスを単一のコアボックスに効果的に切り替えることができます。これはLinuxカーネルのスケーラビリティの主な制限です。 CPU コアを 2 つから 4 つに増やすと、Linux システムの速度がほぼ 2 倍になる可能性がありますが、16 から 32 に至るほとんどのワークロードでは倍増しません。

答え2

スピンロックは、プロセスが削除するロックを継続的にポーリングする場合です。プロセスは(通常)不必要にサイクルを消費するため、これは悪いと見なされます。これは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

通常、スピンロックを防ぐ方法があります。この特定の例には、次のLinuxツールがあります。inotify待つ(通常はデフォルトではインストールされません。)Cで書かれている場合は、次のものを使用できます。inotifyLinuxが提供するAPIです。

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

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

答え3

スレッドがロックを取得しようとしたときに失敗した場合は、3つのことが発生する可能性があります。試してブロックし、試して続行し、試してからスリープモードに切り替えて、何かが発生した場合は、オペレーティングシステムに目覚めさせるように指示できます。 。

今や試してみるのをブロックするよりも、試して続けるのにはるかに少ない時間がかかります。 「試し続ける」に1時間単位がかかり、「試みてブロック」に100時間単位がかかるとしばらく仮定しましょう。

それでは、スレッドがロックを維持するのに平均4単位の時間がかかるとしましょう。 100個を待つのは無駄です。したがって、「試して続行」ループを作成できます。第4の試みでは、一般にロックを得る。これはスピンロックです。スレッドがロックを獲得するまで所定の位置で回転し続けるので、これを呼び出します。

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

1つのスレッドがロックを永遠に保つことがわかっている場合(たとえば、200単位)、試してみて、続行するたびにコンピュータの時間を無駄にします。

したがって、最終的には、スピンロックは非常に効率的であるか、非常に無駄になる可能性がある。ロックを維持するのにかかる「通常の」時間が、「試行とブロック」にかかる時間よりも長い場合は無駄です。ロックが維持される一般的な時間が「試行とブロック」時間よりはるかに低い場合に有効です。

Ps:スレッドについて読むことができる本は「A Thread Primer」です。まだ見つかったら。

答え4

スピンロックは、スケジューラを無効にし、ロックが獲得された特定のコアを中断(irqsaveバリアント)して動作するロックです。スケジューリングを無効にするという点でミューテックスとは異なるため、スピンロックを維持しながらスレッドのみを実行できます。ミューテックスを使用すると、スケジュールされている間に優先順位が高い他のスレッドをスケジュールできますが、同時に保護された部分を実行することはできません。 spinlockはマルチタスクを無効にするため、spinlockを取得してからミューテックスを取得したい他のコードを呼び出すことはできません。 spinlockセクション内のコードは決してスリープモードではありません(コードは通常ロックされたミューテックスやヌルセマフォに会うとスリープモードに切り替わります)。

ミューテックスとの別の違いは、スレッドが通常ミューテックスのために待機するため、ミューテックスの下にキューがあることです。そして、スピンロックは、他のスレッドが実行される必要がある場合でも実行されないことを保証します。したがって、ファイルの外側にあり、スリープ状態にあるかどうかわからない関数を呼び出すときにスピンロックを維持しないでください。

割り込みとスピンロックを共有するには、irqsaveバリアントを使用する必要があります。これはスケジューラを無効にするだけでなく、割り込みも無効にします。これは言う?スピンロックは他の何も実行しないようにすることで動作します。割り込みを実行したくない場合は、割り込みを無効にして安全に重要なセクションに入ることができます。

マルチコアシステムでは、スピンロックは実際にロックを保持している他のコアが解放されるのを待って回転します。このスピンはマルチコアシステムでのみ発生します。シングルコアシステムでは発生しないためです(スピンロックを押し続けるか、ロックが解除されるまで実行されません)。

スピンロックは、意味がある場合は無駄ではありません。非常に小さな重要なセクションでは、相互に排他的なタスクのキューを割り当てることは、重要なタスクを完了するために数マイクロ秒間スケジューラを停止するよりも無駄です。スリープ操作が必要な場合、またはio操作中にロックを維持する必要がある場合(おそらくスリープ)、ミューテックスを使用してください。もちろん、スピンロックをロックしてから割り込み内に解放しようとしないでください。これが機能している間、Arduinoのwhile(flagnotset)。この場合、セマフォを使用してください。

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

関連情報