温馨提示×

ubuntu 为啥会有僵尸进程

小樊
36
2026-08-23 19:13:26
栏目: 智能运维

在 Ubuntu(以及所有 Linux 系统)里,僵尸进程(Zombie / defunct)本质上不是 bug,而是进程生命周期的正常现象。它的出现通常说明:子进程已经结束,但父进程还没有回收它的状态信息

下面从原理、常见原因、是否正常、以及如何处理 4 个角度说明。


一、僵尸进程是什么?

  1. 进程结束的正常流程

    • 子进程调用 exit() 或被信号杀死
    • 内核保存它的退出状态(PID、退出码等)
    • 父进程调用 wait() / waitpid() 读取这个状态
    • 内核才真正回收该进程表项
  2. 僵尸进程的本质

子进程已经死了,但父进程还没“收尸”

此时进程:

  • 不再占用 CPU、内存
  • 仍然占用一个 PID
  • 状态显示为 Z(zombie)

二、为什么 Ubuntu 上会出现僵尸进程?

1️⃣ 父进程没调用 wait()(最常见)

很多程序:

  • 创建子进程后忘了回收
  • 或只关心子进程是否能启动,不管它是否结束

示例(C):

pid_t pid = fork();
if (pid == 0) {
    // 子进程
    exit(0);
}
// 父进程没调用 wait()

ps 看:

ps aux | grep Z

2️⃣ 父进程太忙 / 阻塞

即使父进程本意是想回收,但如果:

  • 父进程在死循环
  • 或阻塞在 I/O
  • 或没正确处理 SIGCHLD

也可能导致僵尸进程累积。


3️⃣ 父进程是 init / systemd(特殊情况)

  • 如果父进程 先死掉
  • 子进程会被 init(PID 1)接管

Ubuntu 默认是 systemd,理论上它会回收孤儿进程:

systemd

但:

  • 如果 systemd 本身异常
  • 或子进程是容器、namespace 内进程
  • 或 PID namespace 异常

也可能出现僵尸。


4️⃣ 程序 bug / 设计问题

常见场景:

  • 长期运行的服务(daemon)
  • 频繁 fork() 的网络服务
  • 脚本疯狂创建子进程但不回收

例如:

  • shell 脚本里 cmd & 但没 wait
  • Python subprocess.Popen 但没 communicate()

三、僵尸进程是不是问题?

✅ 通常问题不大

  • 不占 CPU
  • 不占内存
  • 只占一个 PID

⚠️ 但数量多了就有问题

  • PID 被耗尽(尤其服务器)
  • 说明程序设计不健康

四、如何排查和处理?

1️⃣ 查看僵尸进程

ps aux | awk '$8=="Z"'

或:

ps -eo pid,ppid,stat,cmd | grep Z

你会看到:

PID   PPID  STAT  CMD
1234  1000  Z     [xxx] <defunct>

2️⃣ 找到“不负责任的父进程”

PPID,那个进程就是造成僵尸的元凶


3️⃣ 正确解决方式(不是 kill 僵尸)

不能 kill 僵尸进程

它已经死了,kill 没用

✅ 正确做法:

✅ 方法一:让父进程回收

  • 修改程序
  • 或在父进程里:
    • 调用 wait()
    • 或处理 SIGCHLD

✅ 方法二:重启父进程

kill -HUP 父进程PID

或:

systemctl restart 服务名

五、开发层面如何避免?

C / C++

signal(SIGCHLD, SIG_IGN);

或:

waitpid(-1, NULL, WNOHANG);

Python

subprocess.Popen.communicate()

Shell

cmd &
wait

六、一句话总结

Ubuntu 出现僵尸进程,不是系统问题,而是“父进程没回收子进程”的正常现象。

如果你愿意,可以把:

  • ps -ef
  • 或相关服务名

贴出来,我可以帮你具体判断是哪个程序造成的、该不该管、怎么修

0