Linux 僵尸进程怎么产生和清理?
答案:僵尸进程是"已退出、但父进程没回收"的子进程(STAT 为 Z,名字带 defunct)。kill 僵尸本身无效,要让它的父进程 wait 回收,或杀掉/重启父进程,让它被 PID 1 接管后自动回收。
一、先确认是不是僵尸
ps aux | awk '$8 ~ /Z/ {print}' # 列出 Z 状态进程
ps -ef | grep defunct | grep -v grep # defunct 是僵尸的典型命令名
top # 顶部 Tasks 行会显示 ... zombie,可快速看到总数僵尸的
STAT是Z(可能带<Z+),CMD列显示[java] <defunct>之类。它不占 CPU、几乎不占内存(只剩一个 task_struct 记账),所以top里看不到它吃资源。
二、关键:找到它的父进程 PPID
ps -o pid,ppid,stat,cmd -p 12345
# PID PPID STAT CMD
# 12345 9876 Z+ [worker] <defunct>
ps -p 9876 -o pid,cmd # 9876 就是没回收子进程的父进程
pstree -p 9876 # 看父进程下是不是挂了一串僵尸三、为什么 kill 僵尸没用、该怎么清
kill -9 12345 # 无效:它已经退出,无法再杀
# 正确顺序:
kill -CHLD 9876 # 1) 提示父进程去回收子进程(若父程序正确处理 SIGCHLD 会立即清)
# 若父程序有 bug 不处理,只能:
kill -TERM 9876 # 2) 优雅停父进程;必要时 kill -9 9876
# 父进程一死,僵尸成为孤儿,被 PID 1(systemd/init)收养并立刻回收| 做法 | 是否有效 |
|---|---|
kill -9 僵尸PID | ❌ 已死,无法杀 |
给父进程发 SIGCHLD | 父程序正常处理时 ✅ |
| 重启/修复父程序 | ✅ 根治 |
| 杀掉父进程让 PID 1 收养 | ✅ 兜底 |
僵尸的本质是父程序的 bug:创建了子进程却没有
wait(),或没设置/处理SIGCHLD。临时清掉父进程只是止血,根治必须改父程序(显式 wait、注册SIGCHLDhandler,或把 SIGCHLD 置为SIG_IGN/SA_NOCLDWAIT让内核自动回收)。
四、危害与场景
- 单个/少量僵尸无害,系统正常。
- 短时间产生大量僵尸(如一个循环 fork 但不 wait 的服务)会耗尽 PID 号段,之后任何创建进程的操作报
fork failed: Resource temporarily unavailable,服务彻底无法拉起子进程。 - 常见于:脚本/程序
system()调外部命令后不回收、容器 1 号进程不具备 init 的回收能力(僵尸会堆积——所以容器镜像常用tini/dumb-init做 1 号进程,这也是容器性能里的一个坑)。
FAQ
Q: 什么是僵尸进程? A: 子进程已退出但父进程没 wait 回收,保留 Z 状态记账,占 PID 不占 CPU/内存,命令名显示 defunct。
Q: kill 僵尸为什么没用? A: 它已经死了,信号无效。要让父进程回收,或让父进程退出由 PID 1 接管回收。
Q: 怎么找僵尸和父进程? A: ps aux | awk '$8~/Z/' 找僵尸,ps -o pid,ppid,stat,cmd -p PID 看 PPID。
Q: 怎么清理? A: 给父进程发 SIGCHLD 促使回收;父程序有 bug 则重启或杀父进程被 init 收养;根治要修代码处理 SIGCHLD/wait。
Q: 多了有什么危害? A: 耗尽 PID 导致无法 fork,说明父程序有回收 bug;容器内可用 tini/dumb-init 做 1 号进程。
一句话总结:僵尸进程是已退出但父进程没 wait 回收的 Z/defunct 进程,只占 PID;kill -9 对它无效,正确做法是让父进程处理 SIGCHLD 回收或杀掉父进程由 PID 1 接管,根治靠修父程序(容器用 tini/dumb-init 兜底回收)。