僵尸进程是已终止但未被父进程回收的进程,不占cpu却占用pid和pcb内存,需通过处理父进程(如发送sigchld或kill父进程)来清理,无法用kill -9直接终止。

僵尸进程不是“活着但不动”的进程,而是已经结束、只等父进程收尸的进程。它不占CPU,但会卡住进程号(PID)和内核中的一小块PCB内存。关键点在于:僵尸进程无法被kill -9杀死,真正要动的是它的父进程。
僵尸进程是怎么产生的
核心条件只有一个:子进程已退出,父进程却没调用 wait() 或 waitpid() 去读取它的退出状态。
- 父进程忙于其他逻辑(比如死循环),一直没走到 wait 调用处
- 父进程压根没处理 SIGCHLD 信号,也没显式忽略(signal(SIGCHLD, SIG_IGN))
- 父进程在 fork 后直接 exit,没给子进程留回收机会(极少见,但可能)
- 父进程崩溃或被强制终止,但子进程还没来得及被 init 接管就已退出——这种情况较少,多数孤儿进程会被 init 自动清理
怎么快速发现僵尸进程
最常用且直观的方式是看 top 命令第二行末尾的 Zombie 数字;或者用 ps 直接筛选:
ps aux | awk '$8 ~ /Z/ {print}'
或者更精准地查出僵尸进程及其父进程ID:
ps -eo stat,ppid,pid,comm | grep '^[Zz]'
输出中第二列是 PPID(父进程PID),这是后续操作的关键。
清理僵尸进程的可靠方法
记住:你不能 kill 僵尸本身,只能让它的父进程完成回收动作,或让 init 接管后自动清理。
- 如果父进程是可控的服务(比如你自己写的程序),检查代码是否漏了 wait/waitpid,补上即可
- 如果父进程还在运行但长期不回收(如 bug 导致 wait 没被执行),可向其发送 SIGCHLD 信号尝试唤醒回收逻辑:kill -s SIGCHLD
- 若父进程无响应或不可修复,最有效办法是 kill 它的父进程(即僵尸的 PPID):kill -9
。该父进程退出后,僵尸会变成孤儿进程,被 PID 1 的 init 进程自动收养并清理 - 注意:不要 kill PID 1(init/systemd),否则系统将不稳定
如何避免僵尸进程反复出现
从编程和运维两个层面预防:
- 在 C/C++ 程序中,fork 后务必对子进程做 wait 处理;或注册 SIGCHLD 信号处理器,在其中调用 waitpid(-1, &status, WNOHANG)
- 使用 daemon 化脚本时,确保父 shell 进程不会意外提前退出,导致子进程失去回收主体
- 在容器环境中(如 Docker),主进程(PID 1)必须能正确处理子进程退出,否则容易堆积僵尸;可用 dumb-init 或 tini 作为 init 替代品
- 定期监控:watch -n 5 'ps aux | awk '\''$8 ~ /Z/ {print}'\'' 可实时观察










