当我killall -9 name
杀死一个程序时,状态就变成僵尸了。几分钟后,它真的停了。那么,这几分钟里发生了什么?
答案1
该程序实际上永远不会接收到 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 操作系统中出现僵尸问题该怎么办,有一个半开玩笑的描述:“你无法为僵尸本身做任何事情,因为它们已经死了。相反,杀死邪恶的僵尸大师!"(即麻烦僵尸的父进程)