私はbuntu 14.04を使用していますが、この動作が発生しているので、理解できないようです。
yes
コマンドを実行します(デフォルトのシェルでは:Bash)yes
jobs
を実行します。出力:[1]+ Stopped yes
kill -9 %1
を実行してyes
を停止します。出力:[1]+ Stopped yes
jobs
を実行します。出力:[1]+ Stopped yes
これは、Parallels仮想マシンで実行されているUbuntu 3.16.0-30-generic
にあります。
kill -9
コマンドがyesコマンドを終了しなかったのはなぜですか? [〜#〜] sigkill [〜#〜]捕まえたり無視したりできないと思いましたか?そして、どうすればyesコマンドを終了できますか?
中断されたプロセスのシグナルはブロックされます。ターミナルの場合:
$ 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
するまで実際に信号を送信していないか、カーネルのバグを発見しました。
ファンキーなことは何も起こっていません。ここにはカーネルのバグはありません。これは、Bourne AgainShellおよびマルチタスクオペレーティングシステムからの完全に正常な動作です。
覚えておくべきことは、SIGKILL
への応答であってもプロセスが自分自身を殺すであることです。ここで起こっていることは、Bourne Again Shellが物事に取り掛かっているということですbefore自分自身を殺すように言ったプロセスは、自分自身を殺すことになります。
yes
がSIGTSTP
で停止され、Bourne Againシェルでkill
コマンドを実行したところから何が起こるか考えてみましょう。
SIGKILL
をyes
プロセスに送信します。yes
プロセスは実行するようにスケジュールされており、すぐに強制終了します。あなたが1つのものを見て、他の人が別のものを見ている理由は、実行可能な2つのプロセス間の単純な競争であり、その勝者は、マシンごとに、そして時間とともに変化するものに完全に依存します。 CPUが仮想であるという事実と同様に、システム負荷によって違いが生じます。
興味深いケースでは、ステップ2の詳細は次のとおりです。
kill
コマンドの内部の一部として、次の使用可能なポイントで、そのジョブテーブルのエントリを通知メッセージが必要としてマークします。kill
コマンドを終了し、プロンプトを印刷する直前に、ジョブに関する通知メッセージを印刷する必要があるかどうかを再度確認します。yes
プロセスは、シェルに関する限り、ジョブがまだ停止状態にある限り、まだ自分自身を強制終了する機会がありませんでした。そのため、シェルはそのジョブの「停止」ジョブステータス行を出力し、通知保留フラグをリセットします。yes
プロセスはスケジュールされ、自殺します。重要なポイントは次のとおりです。
SIGKILL
は魔法ではありません。プロセスは、カーネルモードからアプリケーションモードに戻るときに保留中の信号をチェックします。これは、ページフォールト、(ネストされていない)割り込み、およびシステムコールの終了時に発生します。唯一の特別な点は、カーネルがSIGKILL
への応答としてのアクションを即時無条件の自殺以外にすることを許可せず、アプリケーションモードに戻らないことです。重要なことに、プロセスは両方ともカーネルからアプリケーションモードへの移行を行う必要がありますおよびシグナルに応答するために実行するようにスケジュールされます。set -o notify
を使用しない限り)。これらは、シェルが実行サイクルのポイントに到達したときに出力され、保留中の通知があるかどうかを確認します。kill
によって、もう1回はSIGCHLD
シグナルハンドラによって設定されています。これは、シェルがyes
プロセスが自分自身を強制終了するように再スケジュールされる前に実行されている場合、twoメッセージを表示できることを意味します。 1つは「停止」メッセージ、もう1つは「強制終了」メッセージです。/bin/kill
プログラムはシェルの内部ジョブテーブルにアクセスできません。したがって、/bin/kill
ではそのような動作は見られません。通知保留フラグは、SIGCHLD
ハンドラーによって一度だけ設定されます。kill
プロセスをyes
した場合、この動作は表示されません。システムで何かファンキーなことが起こっている可能性があります。私のレシピでは、-9
の有無にかかわらずうまく機能します。
> yes
...
^Z
[1]+ Stopped yes
> jobs
[1]+ Stopped yes
> kill %1
[1]+ Killed yes
> jobs
>
jobs -p
でpidを取得し、root
として強制終了してみてください。
あなたが観察しているのは、このバージョンの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が「Killed」を報告することを期待するかもしれませんが、競合状態があるため、これは保証されません。シグナルは非同期で配信されます。カーネルがシグナルの配信先のプロセスを特定するとすぐに、実際に配信されるのを待たずに、kill
システムコールが返されます。 bashがサブプロセスのステータスをチェックし、まだ死んでいないことを確認し(wait4
は子の死を報告しません)、プロセスが次のようになっていることを印刷する時間がある可能性があります。まだ止まった。何が問題なのかというと、次のプロンプトの前にシグナルが配信されましたが(ps
はプロセスが停止していることを報告します)、bashはまだwait4
を呼び出していません(それだけでなく、それでもジョブは「停止」として報告されますが、ゾンビがプロセステーブルにまだ存在しているためです)。実際、bashは、次にwait4
を呼び出す必要があるときに、他の外部コマンドを実行したときにのみゾンビを刈り取ります。
バグは断続的であり、bashがトレースされている間は再現できませんでした(おそらく、bashが高速に反応する必要がある競合状態であるためです)。 bashチェックの前にシグナルが配信された場合、すべてが期待どおりに行われます。