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


8

私はUbuntu 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

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

kill -9コマンドでyesコマンドが終了しなかったのはなぜですか?SIGKILLを捕まえることも無視することもできないと思いましたか?そして、どうすればyesコマンドを終了できますか?


1
それは面白い。SIGKILLは動作し、私のLinux Mint 17でも動作します。他の信号の場合は、通常、SIGCONTを後で送信して、停止したターゲットで信号が受信されることを確認する必要があります。
PSkocik 2015年

一時停止されているプロセスに対して、bashは本当に「停止」を出力しますか?
edmz

カーネルバージョン(uname -a)をお願いします
roaima

Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux。Parallels DesktopでUbuntuを実行しています。
s1m0n 2015年

1
@blackほとんどのシェルは「停止」と言います。tcshは「中断」と言い、zshは「中断」と言います。表面的な違い。やや重要なのは、bashがSTOPとTSTPに同じメッセージを出力するという事実です。この場合、他のすべてのシェルがSTOPメッセージの注釈にマークを付ける(signal)ため、違いがわかります。

回答:


10

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

$ yes
...
y
y
^Zy

[1]+  Stopped                 yes

2番目のターミナル:

$ killall yes

最初のターミナルで:

$ jobs
[1]+  Stopped                 yes

$ fg
yes
Terminated

ただしSIGKILL、ブロックすることはできません。killall -9 yes2番目の端末から同じことを行うと、すぐに次のようになりyesます:

[1]+  Killed                  yes

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


4
背景の詳細​​:端末でCtrl + Zを発行すると、bashはSIGTSTP(ブロック可能なバージョンのSIGSTOP)をアクティブなプロセスに送信します。これにより、プロセスは、カーネルがスケジュールしないフリーズ状態になります。これはまた、シグナル処理(SIGCONTプロセスをフリーズ解除するシグナルを除く)を抑制し、プロセスがすぐに強制終了されるのを防ぎます。
mreithub 2015年

1
他のシグナルとは異なり、SIGKILLは中断されたプロセスに対してブロックされません。中断されたプロセスにKILLシグナルを送信すると、プロセスは強制終了されます—非同期ですが、実際には基本的に即時です。
Gilles「SO-邪悪なことをやめなさい」

1
@ギレスそれは私が上で説明しようとしていたものです:SIGTERMブロックされてSIGKILLいますが、ブロックされていません。とにかく、OPからのコメントによると、問題jobsはプロセスが死んだことを検出しないことであり、プロセスがkillされていないことではないようkill -9 %1です。
lcd047

1
しかし、自分のシステム(Debian、amd64、bash 4.3.30)でs1m0nの動作を再現できます。
Gilles「SO-邪悪なことをやめよ」

1
一方でSIGKILLブロックすることができない、それは意味のある時間内に配信される保証はありません。たとえば、ブロックI / Oが保留されているプロセスが中断されている場合、プロセスがSIGKILL起動するまで到着しません。I / Oが発生しない場合は、これが発生することはありません。
sapi

7

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

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

覚えておかなければならないのは、プロセスがに応答してもそれ自体を殺すことSIGKILLです。ここで起こっているのは、Bourne Againシェルが、自分自身を殺すように言ったプロセスが自分自身を殺すために取り掛かる前に、物事を回避しているということです。

yes停止し、Bourne AgainシェルでコマンドをSIGTSTP実行したところから何が起こるかを考えkillます。

  1. シェルSIGKILLyesプロセスに送信します。
  2. 並行して
    1. yesプロセスが実行するようにスケジュールし、すぐに自分自身を殺しています。
    2. Bourne Againシェルは続行し、別のプロンプトを出します。

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

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

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

重要な点は次のとおりです。

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

3
それは興味深い理論ですが、OPはタイプするようにjobsなり、シェルはまだプロセスが生きていると見なします。これは、非常に長いスケジュールの競合状態です。:)
lcd047

3
まず第一に、あなたの精巧な答えに感謝します!私は確かに理にかなっており、かなり多くのことを解決しています。しかし、前述のように、乗算jobsコマンドを実行できますが、その後、killすべてがプロセスが停止したことを示しています。しかし、あなたは私に実験を続けるように促し、私はこれを発見しました。メッセージ[1]+ Terminated yesは、別の外部コマンド(echoまたはのような組み込みシェルではない)を実行するとすぐに出力されますjobs。だから私はjobs好きなだけ走ることができ、それは印刷し続けます[1]+ Stopped yes。しかしls、たとえば実行するとすぐに、Bashは印刷します[1]+ Terminated yes
s1m0n

lcd047は質問へのコメントを読みませんでした。これは重要であり、質問の最初に適切に編集されるべきでした。ホストオペレーティングシステムが過負荷になり、ゲストが奇妙なスケジュールを設定しているように見えることがあります。これと同じように、他にも。(私はかつて、暴走するBingデスクトップがホストのCPU時間のほとんどを消費するというかなり奇妙なスケジューリングを引き起こすことに成功しました。)
JdeBP

1
@ギレス問題はjobs、プロセスが実際に停止したことに気付かないことのようです...しかし、別のコマンドを実行して更新されているステータスをどうするかわかりません。
lcd047

1
Gillesでさえコメントを見なかった。これが、コメントに埋め込むのではなくこの種の重要なものを質問に含める必要がある理由です。ジルは、その答えは明らかに遅れについて語っ提供信号を、ないの遅れ送ることを。それらを間違えました。また、質問者のコメント(および実際にここに記載されている箇条書き)を読んで、作成している非常に重要な誤った基本的な仮定を確認してください。仮想プロセッサは必ずしもロックステップで実行する必要はなく、魔法のように常にフルスピードで実行することもできません。
JdeBP 2015年

2

お使いのシステムでファンキーなことが起こっている可能性があります。私のものでは、レシピありとなしの両方でうまく機能します-9

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

pidを取得して、jobs -pそれをkillしてみてくださいroot


使用しているディストリビューション/カーネル/ bashのバージョンを尋ねてもいいですか?多分あなたのbashの内部killコマンドはさらに一歩進んで、ジョブがフリーズしているかどうかをチェックします(ジョブのPIDを見つけて、を使用してenv kill <pid>それを強制終了することをお勧めします。そうすることでkill、bashビルトインではなく実際のコマンドを使用することになります。
mreithub 2015年

opensuse 13.2のbash-4.2-75.3.1.x86_64 キルcmdが内部のものではない:which kill /usr/bin/kill
ダンCornilescu

1
whichはbash-builtinではないので、which <anything>常に実際のコマンドへのパスを提供します。しかし、比較してみてくださいkill --help対を/usr/bin/kill --help
mreithub 2015年

ああ、そうです。確かに、それは組み込みkillです。
Dan Cornilescu 2015年

2

観察しているのは、このバージョンの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チェックの前に配信される場合、すべてが期待どおりに発生します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.