僵尸进程Zombie Process
僵死进程就是一个已经死了但还存在的子进程,就是因为他的父进程没有给他收尸
孤儿进程就是一个没有父进程的子进程
僵尸进程(Zombie Process)是一个已经执行完毕,但它的父进程还没有读取其退出状态,因此它在系统进程表中仍然保留着一个条目。
为了让你更容易理解,我用一个生活中的比喻来解释:
父亲和孩子的比喻
创建子进程 (fork):
想象一下,你(父进程)让你的孩子(子进程)去楼下小卖部买瓶酱油(执行一个任务)。子进程完成任务 (exit):
孩子买完酱油回来了,站在家门口。他已经完成了任务,从某种意义上说,他的“使命”已经结束了。他敲门,想告诉你:“爸爸,我回来了,酱油买到了!”(这个“买到了”或“没买到”就是退出状态)。父进程的责任 (wait):
作为父亲,你应该去开门,接过酱油,并对孩子说声“知道了,辛苦了”(这个行为就是调用 wait() 或 waitpid() 系统调用,来获取子进程的退出状态)。一旦你这样做了,孩子就可以愉快地去玩了,这件事就彻底结束了。僵尸状态的产生:
问题出在哪里呢?如果你(父进程)因为太忙或者程序有Bug,一直没去开门,也从不问孩子任务完成得怎么样。这时,孩子就一直被“卡”在门口。他已经死了(任务完成了,不再消耗CPU、内存等资源)。
但他又没有完全消失(因为他还要等你来“收尸”,即读取他的退出状态)。
这个**“死了但没有完全消失”的状态,就是僵尸状态**。
僵尸进程的技术要点
状态:在 top 或 ps 命令中,它的状态通常显示为 Z (Zombie)。
资源消耗:僵尸进程不占用CPU,也不占用内存。它唯一占用的资源就是进程表中的一个条目(Entry),里面记录了它的进程ID(PID)、退出状态等信息。
为什么不能直接 kill -9?:你不能杀死一个已经“死”了的东西。kill 命令是用来终止正在运行的进程的。因为僵尸进程已经停止运行,所以 kill 对它无效。
僵尸进程有害吗?
单个或少量僵尸进程是无害的。它只是占用了进程表里一个很小的位置,通常不会对系统造成影响。
大量僵尸进程是有害的。如果一个父进程有Bug,不断地创建子进程,而这些子进程结束后都变成了僵尸进程,它会逐渐耗尽系统进程表。当进程表满了之后,系统就无法创建任何新的进程了(包括你尝试登录的ssh连接或者执行任何新命令),这会导致系统瘫痪。
因此,僵尸进程本身不是问题,但它的存在标志着它的父进程有程序缺陷。
如何处理僵尸进程?
处理僵尸进程的唯一方法是处理它的父进程。
找到父进程:使用命令 ps -o ppid,pid,stat,command 查看所有进程,找到僵尸进程(状态为 Z)和它的父进程ID(PPID)。
解决办法:
根本解决:修改父进程的源代码,让它正确地调用 wait() 或 waitpid()来处理子进程的退出。
临时解决:杀死(kill)父进程。当父进程被杀死后,它的所有子进程(包括那个僵尸进程)都会变成“孤儿进程”。系统中的“孤儿”会自动被 init 进程(PID为1)领养。init 进程会周期性地检查并清理它领养的所有僵尸子进程,从而让僵尸进程彻底消失。