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