程序发送SIGKILL信号后会做什么?


39

当我过去killall -9 name杀死一个程序时,状态就变成了僵尸。几分钟后,它真的停止了。那么,在那几分钟内发生了什么?

Answers:


66

该程序实际上从未接收到SIGKILL信号,因为SIGKILL完全由操作系统/内核处理。

发送针对特定进程的SIGKILL后,内核的调度程序将立即停止为该进程提供更多的CPU时间来运行用户空间代码。如果进程在调度程序做出此决定时有任何线程在其他CPU /内核上执行用户空间代码,则这些线程也将停止。(在单核系统中,这过去要简单得多:如果系统中唯一的CPU核正在运行调度程序,那么从定义上讲它并不会同时运行进程!)

如果在SIGKILL时进程/线程正在执行内核代码(例如,系统调用或与内存映射文件相关联的I / O操作),则将变得有些棘手:只有某些系统调用可中断,因此内核在内部将进程标记为处于特殊的“濒死”状态,直到解决系统调用或I / O操作为止。解决这些问题的CPU时间将照常安排。可中断的系统调用或I / O操作将检查调用它们的进程是否在任何适当的停止点死亡,并且在这种情况下将提前退出。不间断的操作将完成,并且将在返回用户空间代码之前检查“垂死”状态。

一旦解决了任何进程中的内核例程,进程状态就会从“濒死”更改为“死亡”,并且内核开始清理它,类似于程序正常退出时的情况。清理完成后,将分配大于128的结果代码(以指示该进程已被信号杀死;有关混乱的详细信息请参见此答案),然后该进程将转换为“僵尸”状态。终止进程的父进程将收到SIGCHLD信号通知。

结果,该过程本身将永远没有机会实际处理它已收到SIGKILL的信息。

当进程处于“僵尸”状态时,表示该进程已死,但其父进程尚未通过使用wait(2)系统调用读取该死进程的退出代码来确认这一点。基本上,僵尸进程消耗的唯一资源是进程表中的一个插槽,该插槽中包含该进程的PID,退出代码以及该进程死亡时的其他“重要统计信息”。

如果父进程在其子进程之前死亡,那么孤立的子进程将被PID#1自动采用,PID#1承担继续调用的特殊职责,以使wait(2)任何孤立的进程都不会像僵尸一样死守。

如果清理僵尸进程需要花费几分钟,则表明僵尸的进程正在挣扎或无法正常工作。

关于在类似Unix的操作系统中出现僵尸问题的情况下,如何进行粗俗的描述:“您不能为僵尸本身做任何事情,因为它们已经死了。相反,请杀死邪恶的僵尸大师! ” (即麻烦的僵尸的父进程)


5
发送SIGKILL时,如果进程在内核调用中(例如执行I / O),会发生什么情况?
gidds '18

9
@gidds要么取消I / O以便执行SIGKILL,要么将SIGKILL延迟到I / O完成。这是“ S”和“ D”睡眠状态之间的区别ps:“ S”用于I / O等待内核可以取消以便传递信号,而“ D”等待它不能发送的信号。
zwol

6
说时间表立即停止给进程分配CPU时间并不完全准确。信号处理的内核端仍由该进程执行,但是该进程将仅执行内核代码,因此当您说程序从未收到信号时,您是对的。该进程将执行负责大部分资源清理的内核代码(打开文件,虚拟内存等)。此清理代码的最后一步是将进程状态更改为僵尸并调用调度程序。这样就永远不会再安排它了。
kasperd '18

4
@gidds进程可以处于至少四种不同的状态。此刻它可以正在运行内核代码,也可以在三种不同的睡眠状态之一中处于睡眠状态。除了致命信号外,睡眠状态可以是可中断,不可中断或不可中断的。如果处于不间断睡眠状态,它将在需要时一直处于睡眠状态,只有唤醒后它才有机会死亡。如果它处于其他两个睡眠状态之一,它将立即唤醒并安排有可用的CPU。
kasperd '18

2
@gidds接下来会发生什么取决于它正在运行的内核代码。无论它是否已经在运行,还是必须先被唤醒,然后可以开始运行它当时所在的内核代码,都将被允许继续。内核代码负责通知该进程已终止并采取相应措施。大多数时候,在内核代码中处理该错误的正确方法是从所执行的任何函数中返回一个错误。一旦内核调用堆栈解开,信号处理代码就可以接管返回用户模式之前的工作。
kasperd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.