プロセスがユーザー モードの場合、いつでも中断できます (カーネル モードへの切り替え)。カーネルがユーザー モードに戻ると、保留中のシグナル (SIGTERM
などのプロセスを強制終了するために使用されるシグナルを含む) があるかどうかを確認します。 そして SIGKILL
)。これは、ユーザー モードに戻ったときにのみプロセスを強制終了できることを意味します。
カーネル モードでプロセスを強制終了できない理由は、同じマシン内の他のすべてのプロセスによって使用されるカーネル構造が破損する可能性があるためです (スレッドを強制終了すると、同じプロセス内の他のスレッドによって使用されるデータ構造が破損する可能性があるのと同じです)。 .
カーネルが長時間かかる可能性のある処理 (たとえば、別のプロセスによって書き込まれたパイプを待機する、またはハードウェアが何かを実行するのを待機する) を行う必要がある場合、カーネルは自身をスリープ状態としてマークし、スケジューラを呼び出して別のプロセスに切り替えることでスリープします。プロセス (スリープしていないプロセスがない場合、「ダミー」プロセスに切り替わり、CPU に少し速度を落とすように指示し、ループ (アイドル ループ) に留まります)。
シグナルがスリープ中のプロセスに送信された場合、ユーザー空間に戻る前にウェイクアップして、保留中のシグナルを処理する必要があります。ここでは、2 つの主な睡眠タイプの違いを示します。
TASK_INTERRUPTIBLE
、中断可能なスリープ。タスクがこのフラグでマークされている場合、タスクはスリープ状態ですが、シグナルによってウェイクアップできます。これは、タスクをスリープとしてマークしたコードが可能なシグナルを期待していることを意味し、ウェイクアップ後にそれをチェックしてシステムコールから戻ります。シグナルが処理された後、システム コールが自動的に再起動される可能性があります (その仕組みについては詳しく説明しません)。TASK_UNINTERRUPTIBLE
、途切れない睡眠。タスクがこのフラグでマークされている場合、簡単に再起動できないか、プログラムがシステムコールがアトミックであることを期待しているため、待機しているもの以外によってウェイクアップされることを期待していません。これは、非常に短いことが知られている睡眠にも使用できます。
TASK_KILLABLE
(ddaa の回答でリンクされている LWN の記事に記載されています) は、新しいバリアントです。
これはあなたの最初の質問に答えます。 2番目の質問については、中断できないスリープを回避することはできません。それらは通常のことです(たとえば、プロセスがディスクから/への読み取り/書き込みを行うたびに発生します)。ただし、それらはほんの一瞬しか続かないはずです。それらがずっと長く続く場合、それは通常、ハードウェアの問題 (または、カーネルには同じように見えるデバイス ドライバーの問題) を意味します。デバイス ドライバーは、決して起こらない何かをハードウェアが実行するのを待っています。また、NFS を使用していて、NFS サーバーがダウンしていることを意味する場合もあります (サーバーが回復するのを待っています。問題を回避するために「intr」オプションを使用することもできます)。
最後に、回復できない理由は、カーネルがユーザーモードに戻ってシグナルを配信するかプロセスを強制終了するまで待機するのと同じ理由です。カーネルのデータ構造が破損する可能性があります (割り込み可能なスリープで待機しているコードは、それを知らせるエラーを受け取る可能性があります)。プロセスを強制終了できるユーザー空間に戻ります; 割り込み不可能なスリープで待機しているコードは、エラーを想定していません)。
中断できないプロセスは通常、ページ フォールトに続く I/O を待機しています。
これを考慮してください:
- スレッドが、コアにないページ (デマンド ロードされた実行可能ファイル、スワップ アウトされた匿名メモリのページ、またはデマンド ロードされた mmap() されたファイル) にアクセスしようとしています。ほとんど同じものです)
- カーネルは現在、ロード中です (しようとしています)
- ページが利用可能になるまでプロセスを続行できません。
シグナルを処理できないため、この状態ではプロセス/タスクを中断できません。その場合、別のページ フォールトが発生し、元の場所に戻ります。
「プロセス」と言うとき、本当は「タスク」を意味します。これは Linux (2.6) では大まかに「スレッド」に変換され、/proc に個別の「スレッド グループ」エントリがある場合とない場合があります
場合によっては長時間お待ちいただくこともございます。この典型的な例は、実行可能ファイルまたは mmap 化されたファイルが、サーバーに障害が発生したネットワーク ファイルシステム上にある場合です。 I/O が最終的に成功した場合、タスクは続行されます。最終的に失敗した場合、通常、タスクは SIGBUS か何かを受け取ります。
中断できないプロセスとは、シグナルによって中断できないシステム コール (カーネル関数) 内にたまたまあるプロセスです。
これが何を意味するのかを理解するには、割り込み可能なシステム コールの概念を理解する必要があります。古典的な例は read()
です .これは、ハード ドライブのスピンアップやヘッドの移動を伴う可能性があるため、長時間 (秒単位) かかるシステム コールです。この時間のほとんどの間、プロセスはスリープ状態になり、ハードウェアでブロックされます。
プロセスがシステム コールでスリープしている間に、Unix 非同期シグナル (SIGTERM など) を受信すると、次のことが起こります:
- システム コールが途中で終了し、-EINTR をユーザー空間に返すように設定されています。
- シグナルハンドラが実行されます。
- プロセスがまだ実行中の場合、プロセスはシステム コールから戻り値を取得し、同じ呼び出しを再度行うことができます。
システム コールから早期に戻ることで、ユーザー空間コードはシグナルに応答してその動作を即座に変更できます。たとえば、SIGINT または SIGTERM に反応して正常に終了します。
一方、一部のシステム コールは、この方法で中断することはできません。システム コールが何らかの理由で停止した場合、プロセスは無期限にこの強制終了できない状態のままになる可能性があります。
LWN は 7 月にこのトピックに触れたすばらしい記事を掲載しました。
元の質問に答えるには:
-
これを防ぐ方法:問題の原因となっているドライバーを特定し、使用を中止するか、カーネル ハッカーになって修正します。
-
再起動せずに中断できないプロセスを強制終了する方法:何らかの方法でシステム コールを終了させます。電源スイッチを押さずにこれを行う最も効果的な方法は、電源コードを引っ張ることです。 LWN の記事で説明されているように、カーネル ハッカーになってドライバーに TASK_KILLABLE を使用させることもできます。
3 番目の質問:sudo kill -HUP 1
を実行することで、中断できないプロセスを強制終了できると思います。 実行中のプロセスを終了せずに init を再起動します。実行後、中断できないプロセスはなくなりました。