僵尸进程不占资源,只占PID,需处理其父进程;通过ps或top识别Z状态,定位父进程后按可控性选择回收、重启或发SIGCHLD信号,并应监控PID使用与编码时默认添加回收逻辑。

僵尸进程本身不运行、不占 CPU 和内存,只卡在进程表里占个 PID,真正要处理的不是它,而是它的父进程——那个没给“离职员工办完手续”的主管。
一眼识别:确认是不是真僵尸
别光看名字带 defunct 就认定是僵尸。最靠谱的方式是查状态字段:
-
用这个命令查全量(推荐):
ps -eo stat,ppid,pid,cmd | grep '^[Zz]'—— 只匹配状态开头是 Z 或 z 的行,避开命令行含字母 z 的干扰 -
看 top 第一行:直接留意 zombie 后面的数字,比如
0 zombie就说明当前没有 - 别信 ps aux | grep Z:它会把 vim、zip 这类正常进程也抓进来,纯属误报
顺藤摸瓜:找到它的“爸爸”是谁
僵尸进程自己没法动,关键得知道谁该负责收尸。两步到位:
- 先提取所有僵尸的父进程 PID(去重):
ps -eo stat,ppid,pid | grep '^[Zz]' | awk '{print $2}' | sort -u - 再验证这些父进程是否还活着:
ps -p—— 如果返回空,说明父进程已死,那僵尸早该被 init(PID 1)接管清理了;如果返回结果且状态是 S/R,说明它还在跑,但没尽责-o pid,comm,stat
对症下药:按父进程情况选清理方式
父进程是否可控,决定你该动手改代码,还是重启服务:
-
你能改代码(如自研服务、脚本):在 fork 后加回收逻辑,比如注册 SIGCHLD 信号处理函数,在里面调用
waitpid(-1, &status, WNOHANG)非阻塞轮询;或者干脆开头加signal(SIGCHLD, SIG_IGN),让内核自动回收(代价是拿不到子进程退出码) - 父进程是第三方程序(如老旧 daemon、闭源服务):谨慎重启它。父进程退出后,所有子僵尸会被 init 自动收走。但务必提前评估影响——比如它管着数据库连接池或正在做热更新,贸然 kill 可能引发业务中断
-
父进程疑似卡死或无响应:先尝试发 SIGCHLD 信号提醒它收尸:
kill -s SIGCHLD;没反应再考虑重启
预防为主:别等积成堆才处理
长期运行的服务最容易积累僵尸,尤其高并发 fork 场景。日常可做三件事:
- 监控
/proc/sys/kernel/pid_max和当前已用 PID 数(cat /proc/sys/kernel/pid_max对比ps -e | wc -l),PID 快耗尽时就是预警信号 - 定期跑脚本检查:
ps -eo stat,ppid,pid | awk '$1 ~ /^[Zz]$/ {print $2}' | sort -u | while read ppid; do echo "PPID $ppid: $(ps -p $ppid -o comm= 2>/dev/null)"; done - 新写 C/Python 等多进程程序时,把 wait 或 signal 处理当成标配,就像写 fclose 一样自然











