Linux では、プロセスがディスクからブロックを読み取る必要がある場合、プロセスの状態はどうなりますか? ブロックされていますか?もしそうなら、実行する別のプロセスはどのように選択されますか?
8 に答える
プロセスがディスクからデータを取得する必要がある場合、CPU での実行を効果的に停止して、他のプロセスを実行できるようにします。これは、操作が完了するまでに時間がかかる可能性があるためです。ディスクのシーク時間は少なくとも 5 ミリ秒が一般的であり、5 ミリ秒は 1000 万です。 CPU サイクル、プログラムの観点からは永遠です!
プログラマーの観点から (「ユーザー空間内」とも呼ばれます)、これはブロッキング システム コールと呼ばれます。(同じ名前のシステム コールの薄い libc ラッパーです)を呼び出した場合write(2)
、プロセスはその境界で正確に停止しません。カーネル内で、システム コール コードを実行し続けます。ほとんどの場合、特定のディスク コントローラ ドライバ (ファイル名 → ファイルシステム/VFS → ブロック デバイス → デバイス ドライバ) に到達し、ディスク上のブロックをフェッチするコマンドが適切なハードウェアに送信されます。ほとんどの場合、高速な操作。
その後、プロセスはスリープ状態になります (カーネル空間では、ブロッキングはスリープ状態と呼ばれます。カーネルの観点からは何もブロックされません)。ハードウェアが最終的に適切なデータをフェッチすると、プロセスは起動され、プロセスは実行可能としてマークされ、スケジュールされます。最終的に、スケジューラはプロセスを実行します。
最後に、ユーザー空間では、ブロッキング システム コールが適切なステータスとデータを返し、プログラム フローが続行されます。
ほとんどの I/O システム コールをノンブロッキング モードで呼び出すことができます(およびO_NONBLOCK
を参照)。この場合、システム コールはすぐに戻り、ディスク操作の送信のみを報告します。プログラマーは、操作が正常に完了したかどうかを後で明示的に確認し、その結果を取得する必要があります (たとえば、 を使用)。これは、非同期またはイベントベースのプログラミングと呼ばれます。open(2)
fcntl(2)
select(2)
ここでのD 状態TASK_UNINTERRUPTIBLE
( Linux の状態名で呼び出される)について言及しているほとんどの回答は正しくありません。D状態は、カーネル空間のコード パスでのみトリガーされる特別なスリープ モードであり、そのコード パスを中断できない場合(プログラムが複雑すぎるため)、非常に長い時間だけブロックすることが期待されます。短時間。ほとんどの「D 状態」は実際には見えないと思います。それらは非常に短命であり、'top' などのサンプリング ツールでは観察できません。
いくつかの状況で、D 状態で強制終了できないプロセスに遭遇する可能性があります。NFSはそれで有名で、何度も遭遇しました。常にローカル ディスクに到達し、高速なエラー検出 (SATA では、エラー タイムアウトは約数 100 ミリ秒) を想定している一部の VFS コード パスと、実際にネットワークからデータをフェッチする NFS との間に意味上の衝突があると思います。より弾力性があり、回復が遅くなります (300 秒の TCP タイムアウトが一般的です)。Linux 2.6.25 で導入されたクールなソリューションについては、この記事を参照してくださいTASK_KILLABLE
。この時代の前に、SIGKILL をカーネル スレッドに送信することで実際に NFS プロセス クライアントにシグナルを送信できるハックがありましたが、rpciod
その醜いトリックは忘れてください。
ファイル記述子の戻りを待っている間、read()
またはwrite()
ファイル記述子からの戻りを待っている間、プロセスは「D」または「ディスクスリープ」と呼ばれる特別な種類のスリープになります。このような状態にある間、プロセスを強制終了または中断することはできないため、これは特別です。ioctl() からの戻りを待っているプロセスも、この方法でスリープ状態になります。
これに対する例外は、ファイル (端末やその他のキャラクタ デバイスなど) がO_NONBLOCK
モードで開かれ、デバイス (モデムなど) が初期化に時間がかかると想定されている場合に渡される場合です。ただし、質問でブロックデバイスを示しました。また、非ブロッキング モードで開かれた fd でブロックされる可能性が高い を試したioctl()
ことはありません (少なくとも意図的にではありません)。
別のプロセスがどのように選択されるかは、使用しているスケジューラと、そのスケジューラ内で重みを変更するために他のプロセスが行った可能性がある内容に完全に依存します。
一部のユーザー空間プログラムは、特定の状況下で、再起動するまで永久にこの状態のままになることが知られています。これらは通常、他の「ゾンビ」とグループ化されますが、技術的に機能していないため、この用語は正しくありません。
I / Oを実行するプロセスはD状態(中断できないスリープ)になり、プログラムの実行に戻るようにCPUに指示するハードウェア割り込みが発生するまでCPUを解放します。man ps
他のプロセス状態についてはを参照してください。
カーネルに応じて、実行可能なプロセスのランキューを追跡するプロセススケジューラがあります。これは、スケジューリングアルゴリズムとともに、どのプロセスをどのCPUに割り当てるかをカーネルに指示します。考慮すべきカーネルプロセスとユーザープロセスがあります。各プロセスにはタイムスライスが割り当てられます。これは、使用が許可されているCPU時間のチャンクです。プロセスがすべてのタイムスライスを使用すると、期限切れとしてマークされ、スケジューリングアルゴリズムで優先度が低くなります。
2.6カーネルには、O(1)時間計算量スケジューラがあるため、実行中のプロセスの数に関係なく、CPUは一定時間で割り当てられます。ただし、2.6ではプリエンプションが導入され、CPU負荷分散は簡単なアルゴリズムではないため、より複雑になります。いずれにせよ、それは効率的であり、I/Oを待つ間CPUはアイドル状態のままにはなりません。
プロセスが単一のスレッドであり、ブロッキング I/O を使用していると仮定すると、プロセスは I/O が完了するのを待ってブロックします。カーネルは、ナイスネス、優先度、最終実行時間などに基づいて、その間に実行する別のプロセスを選択します。他に実行可能なプロセスがない場合、カーネルは何も実行しません。代わりに、マシンがアイドル状態であることをハードウェアに通知します (これにより、消費電力が低下します)。
I/O が完了するのを待っているプロセスは、通常、状態 D で表示されps
ますtop
。
はい、タスクは read() システム コールでブロックされます。準備ができている別のタスクが実行されます。他のタスクの準備ができていない場合は、(その CPU の) アイドル タスクが実行されます。
通常のブロッキングディスク読み取りにより、タスクは「D」状態になります(他の人が指摘したように)。このようなタスクは、CPU を消費していなくても、負荷平均に寄与します。
他のタイプの IO、特に tty とネットワークはまったく同じように動作しません。プロセスは「S」状態になり、中断される可能性があり、負荷平均に対してカウントされません。
はい、IO を待っているタスクはブロックされ、他のタスクが実行されます。次のタスクの選択はLinux スケジューラによって行われます。
通常、プロセスはブロックされます。読み取り操作が非ブロッキングとしてマークされたファイル記述子に対して行われる場合、またはプロセスが非同期 IO を使用している場合、ブロックされません。また、プロセスにブロックされていない他のスレッドがある場合、それらは実行を継続できます。
次にどのプロセスを実行するかは、カーネルのスケジューラーによって決定されます。