您正在观察的是此版本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
系统调用或其同级之一来检索其退出状态。
交互式外壳程序应检查是否有死掉的孩子并在打印提示之前对其进行收获(除非另有配置)。在某些情况下,此版本的bash无法做到:
bash-4.3$ jobs -l
[1]+ 3083 Stopped sleep 9999
bash-4.3$ true
bash-4.3$ /bin/true
[1]+ Killed sleep 9999
您可能希望bash在命令后打印提示后立即报告“杀死” kill
,但这不能保证,因为存在竞争条件。信号是异步传递的:kill
内核确定要向哪个进程传递信号后,系统调用将立即返回,而无需等待信号实际传递。在实践中,bash有可能并且确实发生了,bash有时间检查其子进程的状态,发现它仍然没有死(wait4
没有报告任何孩子死亡),并打印该进程仍在停止。出问题的是,在下一个提示之前,信号已传递(ps
报告该进程已死),但bash仍未调用wait4
(我们可以看到,不仅因为它仍将作业报告为“已停止”,而且还因为进程表中仍然存在僵尸)。实际上,当bash wait4
运行其他外部命令时,它仅在下次需要调用时才收获僵尸。
该错误是间歇性的,在跟踪bash时我无法重现该错误(大概是因为这是bash需要快速反应的竞争条件)。如果信号在bash检查之前发出,则一切都会按预期进行。