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