Linux 中的僵尸进程有时也称为失效或死进程。它们是已完成执行的进程,但它们的条目并未从进程表中删除。
Linux 维护着所有正在运行的进程及其状态的进程表。让我们简要概述各种进程状态:
EXIT_ZOMBIE
。当一个进程完成它的工作时,Linux 内核通过发送
SIGCHLD
信号通知退出进程的父进程。然后父进程执行wait()
系统调用来读取子进程的状态并获取退出代码。这也会从进程表中清除子进程的条目,此进程结束。如果父进程没有被编程为在创建子进程时执行
wait()
系统调用,则不会发生清理。在这种情况下,父进程无法监视子进程的状态变化,最终会忽略SIGCHLD
信号。这会导致已完成进程的僵尸状态留在进程表中,因此它作为僵尸进程出现在进程列表中。另一种情况是父进程无法处理或接收来自子进程的
SIGCHLD
信号。这种情况也会导致僵尸的产生。
我们可以使用
ps
命令识别僵尸列表:
$ ps ux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 191040 2828 ? Ss Aug18 14:39 /usr/lib/systemd/systemd --system --deserialize 20
root 2 0.0 0.0 0 0 ? S Aug18 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? S< Aug18 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S Aug18 0:23 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? Z Aug18 0:09 [migration/0]
root 8 0.0 0.0 0 0 ? S Aug18 0:00 [rcu_bh]
Z
在STAT
列使用
awk
命令进一步过滤基于Z
进程状态的输出:
$ ps ux | awk '{if($8=="Z") print}'
shubh 108 0 0 0 0 tty1 Z 16:25 0:00 [zombie] <defunct>
检查僵尸进程的数量和列表的另一种方便的方法是使用
top
命令:
$ top
Tasks: 8 total, 1 running, 6 sleeping, 0 stopped, 1 zombie
%Cpu(s): 7 us, 6 sy, 0 ni, 5 id, 0 wa, 2 hi, 0 si, 0 st
KiB Mem : 8269412 total, 3161228 free, 4878832 used, 229352 buff/cache
KiB Swap: 15483260 total, 14830144 free, 653116 used. 3256848 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 8936 192 148 S 0 0 0:17 init
8 root 20 0 8936 96 56 S 0 0 0:00 init
我们不能真正杀死僵尸进程,因为它已经死了。但是,我们可以使用一些变通方法来清理僵尸进程。
SIGCHLD
信号我们可以手动向僵尸进程的父进程发送
SIGCHLD
信号。因此,它会通知父进程触发wait()
系统调用,这将从进程表中清除已失效的子进程。找到已失效进程的父 ID:
$ ps -A -ostat,pid,ppid | grep -e '[zZ]'
Z 108 103
这列出了僵尸进程的
STAT
列、进程 ID
和父进程 ID
。接下来使用kill
命令向父进程发送SIGCHLD
信号:
kill -s SIGCHLD 103
但是,并不能真正保证向父进程发送
SIGCHLD
信号会杀死僵尸进程。它仅适用于父进程可以处理SIGCHLD
信号的情况。
如果上面无法清除已失效的进程,我们应该考虑杀死其父进程:
kill -9 103
103
是已失效进程的父 ID但是,杀死父进程会影响其所有子进程。因此,我们应该格外小心,并且必须在杀死父进程之前确定影响。
如果存在很多僵尸进程,或者如果僵尸进程的父进程是
init
进程(pid=1
),我们也可以考虑重启系统。