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