web-dev-qa-db-ja.com

SIGKILLが停止したプログラムを終了しないのはなぜですか(はい)?

私はbuntu 14.04を使用していますが、この動作が発生しているので、理解できないようです。

  1. yesコマンドを実行します(デフォルトのシェルでは:Bash
  2. タイプ CtrlZ 停止するにはyes
  3. jobsを実行します。出力:
    [1]+ Stopped yes
  4. kill -9 %1を実行してyesを停止します。出力:
    [1]+ Stopped yes
  5. jobsを実行します。出力:
    [1]+ Stopped yes

これは、Parallels仮想マシンで実行されているUbuntu 3.16.0-30-genericにあります。

kill -9コマンドがyesコマンドを終了しなかったのはなぜですか? [〜#〜] sigkill [〜#〜]捕まえたり無視したりできないと思いましたか?そして、どうすればyesコマンドを終了できますか?

8
s1m0n

中断されたプロセスのシグナルはブロックされます。ターミナルの場合:

$ yes
...
y
y
^Zy

[1]+  Stopped                 yes

2番目の端末:

$ killall yes

最初のターミナル:

$ jobs
[1]+  Stopped                 yes

$ fg
yes
Terminated

ただし、SIGKILLはブロックできません。 2番目のターミナルからkillall -9 yesで同じことを行うと、すぐにyesターミナルで次のようになります。

[1]+  Killed                  yes

その結果、kill -9 %1がプロセスをすぐに終了しない場合、bashは、プロセスをfgするまで実際に信号を送信していないか、カーネルのバグを発見しました。

10
lcd047

慌てる必要はありません。

ファンキーなことは何も起こっていません。ここにはカーネルのバグはありません。これは、Bourne AgainShellおよびマルチタスクオペレーティングシステムからの完全に正常な動作です。

覚えておくべきことは、SIGKILLへの応答であってもプロセスが自分自身を殺すであることです。ここで起こっていることは、Bourne Again Shellが物事に取り掛かっているということですbefore自分自身を殺すように言ったプロセスは、自分自身を殺すことになります。

yesSIGTSTPで停止され、Bourne Againシェルでkillコマンドを実行したところから何が起こるか考えてみましょう。

  1. シェルはSIGKILLyesプロセスに送信します。
  2. 並列
    1. yesプロセスは実行するようにスケジュールされており、すぐに強制終了します。
    2. ボーンアゲインシェルは続行し、別のプロンプトを発行します。

あなたが1つのものを見て、他の人が別のものを見ている理由は、実行可能な2つのプロセス間の単純な競争であり、その勝者は、マシンごとに、そして時間とともに変化するものに完全に依存します。 CPUが仮想であるという事実と同様に、システム負荷によって違いが生じます。

興味深いケースでは、ステップ2の詳細は次のとおりです。

  1. ボーンアゲインシェルは続きます。
  2. 組み込みのkillコマンドの内部の一部として、次の使用可能なポイントで、そのジョブテーブルのエントリを通知メッセージが必要としてマークします。
  3. killコマンドを終了し、プロンプトを印刷する直前に、ジョブに関する通知メッセージを印刷する必要があるかどうかを再度確認します。
  4. yesプロセスは、シェルに関する限り、ジョブがまだ停止状態にある限り、まだ自分自身を強制終了する機会がありませんでした。そのため、シェルはそのジョブの「停止」ジョブステータス行を出力し、通知保留フラグをリセットします。
  5. yesプロセスはスケジュールされ、自殺します。
  6. カーネルは、コマンドラインエディターの実行で忙しいシェルに、プロセスが強制終了したことを通知します。シェルはステータスの変化を記録し、ジョブに通知保留のフラグを付けます。
  7. 押すだけ enter プロンプト印刷を再度繰り返すと、シェルは新しいジョブステータスを印刷する機会が与えられます。

重要なポイントは次のとおりです。

  • プロセスは自殺します。 SIGKILLは魔法ではありません。プロセスは、カーネルモードからアプリケーションモードに戻るときに保留中の信号をチェックします。これは、ページフォールト、(ネストされていない)割り込み、およびシステムコールの終了時に発生します。唯一の特別な点は、カーネルがSIGKILLへの応答としてのアクションを即時無条件の自殺以外にすることを許可せず、アプリケーションモードに戻らないことです。重要なことに、プロセスは両方ともカーネルからアプリケーションモードへの移行を行う必要がありますおよびシグナルに応答するために実行するようにスケジュールされます。
  • 仮想CPUは、ホストオペレーティングシステム上の単なるスレッドです。ホストが仮想CPUの実行をスケジュールしているという保証はありません。ホストオペレーティングシステムも魔法ではありません。
  • ジョブの状態が変化しても、通知メッセージは出力されません(set -o notifyを使用しない限り)。これらは、シェルが実行サイクルのポイントに到達したときに出力され、保留中の通知があるかどうかを確認します。
  • 通知保留フラグが2回設定されています。1回はkillによって、もう1回はSIGCHLDシグナルハンドラによって設定されています。これは、シェルがyesプロセスが自分自身を強制終了するように再スケジュールされる前に実行されている場合、twoメッセージを表示できることを意味します。 1つは「停止」メッセージ、もう1つは「強制終了」メッセージです。
  • 明らかに、/bin/killプログラムはシェルの内部ジョブテーブルにアクセスできません。したがって、/bin/killではそのような動作は見られません。通知保留フラグは、SIGCHLDハンドラーによって一度だけ設定されます。
  • 同じ理由で、別のシェルからkillプロセスをyesした場合、この動作は表示されません。
7
JdeBP

システムで何かファンキーなことが起こっている可能性があります。私のレシピでは、-9の有無にかかわらずうまく機能します。

> yes
...
^Z
[1]+  Stopped                 yes
> jobs
[1]+  Stopped                 yes
> kill %1
[1]+  Killed                  yes
> jobs
> 

jobs -pでpidを取得し、rootとして強制終了してみてください。

2
Dan Cornilescu

あなたが観察しているのは、このバージョンのbashのバグです。

kill -9 %1はジョブをすぐに強制終了します。 psでそれを観察できます。 bashプロセスをトレースしてkillシステムコールがいつ呼び出されたかを確認し、サブプロセスをトレースして信号を受信して​​処理するタイミングを確認できます。さらに興味深いことに、プロセスに何が起こっているのかを確認することができます。

bash-4.3$ sleep 9999
^Z
[1]+  Stopped                 sleep 9999
bash-4.3$ kill -9 %1

[1]+  Stopped                 sleep 9999
bash-4.3$ jobs
[1]+  Stopped                 sleep 9999
bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ 

別の端末:

% ps 3083
  PID TTY      STAT   TIME COMMAND
 3083 pts/4    Z      0:00 [sleep] <defunct>

サブプロセスは ゾンビ です。それは死んでいます:残っているのはプロセステーブルのエントリだけです(ただし、メモリ、コード、開いているファイルなどはありません)。エントリは、親が通知し、 waitシステムコールまたはその兄弟の1つ を呼び出して終了ステータスを取得するまでそのままになります。

インタラクティブシェルは、プロンプトを印刷する前に、死んだ子供をチェックして刈り取ることになっています(特に設定されていない限り)。このバージョンのbashは、状況によってはそれを実行できません。

bash-4.3$ jobs -l
[1]+  3083 Stopped                 sleep 9999
bash-4.3$ true
bash-4.3$ /bin/true
[1]+  Killed                  sleep 9999

killコマンドの後にプロンプ​​トが出力されるとすぐにbashが「Kill​​ed」を報告することを期待するかもしれませんが、競合状態があるため、これは保証されません。シグナルは非同期で配信されます。カーネルがシグナルの配信先のプロセスを特定するとすぐに、実際に配信されるのを待たずに、killシステムコールが返されます。 bashがサブプロセスのステータスをチェックし、まだ死んでいないことを確認し(wait4は子の死を報告しません)、プロセスが次のようになっていることを印刷する時間がある可能性があります。まだ止まった。何が問題なのかというと、次のプロンプトの前にシグナルが配信されましたが(psはプロセスが停止していることを報告します)、bashはまだwait4を呼び出していません(それだけでなく、それでもジョブは「停止」として報告されますが、ゾンビがプロセステーブルにまだ存在しているためです)。実際、bashは、次にwait4を呼び出す必要があるときに、他の外部コマンドを実行したときにのみゾンビを刈り取ります。

バグは断続的であり、bashがトレースされている間は再現できませんでした(おそらく、bashが高速に反応する必要がある競合状態であるためです)。 bashチェックの前にシグナルが配信された場合、すべてが期待どおりに行われます。