当使用 top 命令监控 Linux 系统时,您可能会在 '任务 (Tasks)' 行中发现类似 1 僵尸 (zombie) 的项目。

top - 16:03:19 up  7:09,  1 user,  load average: 2.24, 2.21, 2.29
作 业: 392 总计,   1 运行, 390 等待,   0 暂停,   1 僵尸
...

这个“僵尸”是什么,它会对系统产生什么影响呢?在这篇文章中,我们将明确解释僵尸进程(Zombie Process)的身份以及如何确认和解决它。


僵尸进程(Zombie Process)是什么?🧟



为了便于理解,我们可以将其比喻为进程的生命周期。

  1. 诞生 (Fork): 父进程(Parent Process)创建子进程(Child Process) (fork()).

  2. 执行 (Exec): 子进程执行其任务。

  3. 结束 (Exit): 子进程完成任务并终止 (exit()).

  4. 收获 (Wait): 当子进程终止时,操作系统(内核)在进程表中保留该进程的 PID、结束状态等信息,并向父进程发送 SIGCHLD(子进程已终止)信号。

  5. 父进程接收该信号后,调用 wait() 系统调用以“收获”子进程的结束状态信息。在收获该信息后,内核才会完全从进程表中移除子进程的条目。

僵尸进程 就是卡在第 4 和第 5 步之间的状态。也就是说,子进程已完成执行并终止,但父进程尚未调用 wait() 来收获结束状态

如其名,这种进程处于死亡状态(不在执行中)。因此,它不会消耗 CPU 或内存等系统资源。

僵尸进程为什么会成问题?

僵尸进程本身几乎不占用系统资源,但占用了进程表中的一个插槽(PID)。

如果由于父进程的缺陷等原因,僵尸进程不断堆积而未被清理,那么系统能够分配的 PID 数量(最大值)将会达到上限。在这种情况下,系统将无法再创建新的进程,可能会导致严重的故障。在 top 中看到 1~2 个僵尸并不罕见,但如果这个数字持续增加,则需要采取措施。


如何确认和识别僵尸进程

top 命令只显示僵尸的 _数量_。要确认哪些进程处于僵尸状态,以及它们的父进程是谁,可以使用 ps 命令。

最简单的方法是查找 ps 命令的 STAT(状态)列中标记为'Z' 的进程。

# 详细查看系统的所有进程,过滤出 'Z' 状态(僵尸)的进程
ps -elf | grep ' Z '

# 或者使用 'aux' 选项(第 8 列($8)是状态(STAT))
ps aux | awk '$8=="Z"'

示例输出:

# ps -elf | grep ' Z '
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
0 Z  user  5021  5000  0  80   0 -     0 exit   15:30 ?        00:00:00 [defunct]

以上示例中重要的信息如下:

  • S (状态): Z (表示处于僵尸状态)

  • PID: 5021 (这是僵尸进程的 PID)

  • PPID: 5000 (这是未收获僵尸的父进程的 PID)

  • CMD: [defunct] (表示已终止但未清理的名称)


解决僵尸进程的方法



最重要的一点是僵尸进程无法通过 kill 命令终止

kill -9 5021 (以上示例的僵尸 PID)

上述命令无效,因为僵尸已经处于“死亡”状态。没有处理 kill 信号的主体。

解决僵尸进程的唯一方法是让父进程调用 wait()

第一步:向父进程发送信号(推荐)

首先尝试的方法是向父进程(PPID)手动发送 SIGCHLD 信号,以便让它确认子进程的状态。

# 向以上示例的父 PID(5000)发送 SIGCHLD 信号
kill -s SIGCHLD 5000

这会通知父进程“你的子进程之一已终止,请确认!”如果父进程正常编程,则会接收此信号并清理僵尸。

第二步:强制结束父进程(最后手段)

如果第一步无效,则意味着父进程(PPID 5000)本身已停止,或 wait() 调用逻辑存在严重缺陷。

在这种情况下,强制终止父进程 是唯一的解决方案。

# 终止父进程 (PPID 5000)
kill 5000

# 如果仍然无法终止,则强制终止
kill -9 5000

为什么终止父进程可以解决问题?

在 Linux 中,当父进程死亡时,它的子进程(孤儿进程)会自动被init 进程(PID 1)或 systemd 收养。init 进程定期检查子进程的状态,并设计为立即收获(reap)终止的子进程(包括僵尸)。

因此,如果麻烦的父进程(PPID 5000)死亡,僵尸(PID 5021)将成为 init 的新子进程,init 将立即清理僵尸。

⚠️ 注意:在终止父进程之前,请务必使用 ps -p 5000(父 PID)命令检查该进程是否不是系统的重要服务(例如:数据库、网络服务器等)。强制终止重要服务可能会导致更大的故障。


总结

  • 僵尸进程 是指执行已结束但父进程未收获结束状态的、在进程表中残留的垃圾。

  • 它不消耗资源,但会占用 PID,并且过多则会引发系统故障。

  • 可以使用 ps -elf | grep ' Z ' 命令查找僵尸(PID)及其父进程(PPID)。

  • 解决方案针对的是非僵尸的父进程(PPID)

    1. kill -s SIGCHLD <父PID> (推荐)

    2. kill <父PID> (最后手段)