僵尸进程的由来

要想了解如何避免僵尸进程,首先我们需要知道这个问题从何而来

Linux 系统 进程号的最大值是 32768

当PID总数达到这个值之后,系统将无法创建新的进程,这是一个很严重的问题

我曾经遇到一个系统bug就是由于调用外部系统的时候,外部系统没有返回,进程不会被释放而一直等待,最终导致了 PID 耗尽的bug

关于Linux进程树等问题这里不想再多重复,

正常情况下,进程退出时 PID 将会被 父进程 回收,具体做法是

  1. 子进程结束时,系统向其父进程发送SIGCHILD信号
  2. 父进程调用wait函数后阻塞
  3. 父进程被SIGCHILD信号唤醒然后去回收僵尸子进程

这里存在一个特殊情况是,父进程 早于 子进程结束的情况,例如父进程被 kill

这种情况 子进程的 父进程 会被指向到 init (也就是1号进程),这样1号进程会负责回收

早期的 Linux系统中的init进程非常轻量,叫做 sysinit

但是,在现代的Linux环境中,这个init进程一般会是systemd或者upstartd等重量级的进程,它们会做很多额外的工作。

(实际上这个问题带来了很多争论,部分开发人员认为,使用更重的 init 进程弊大于利)

通过不断的调用 bash,可以得到一个标准的进程树

我们可以看到 3099号进程的 父进程是 3093,它的子进程包括 3105、3111、3117、3123

这里如果 kill 掉 3099号进程,会发现它的直接子进程 3105的父进程确实变成了

/lib/systemd/systemd –user (PID 1634)

虽然确实是 systemd 但是说好的 1号 进程呢?

1号进程是 /sbin/init auto noprompt

想知道为什么?在 Ubuntu18 之后 (我当前是ubuntu20),

/sbin/init 就是 /lib/system/systemd 的一个软链接

在上面这种正常的 Linux 系统上,即使我们 kill 掉了 3105的父进程,它也不会变成僵尸,此时使用 ps -ef | grep defunct 是找不到任何僵尸进程的

来看Docker 的情况

杀死 21号 进程之后, 24号进程会被托管给 docker的1号进程

Docker 的 1号 进程实际上只是一个普通的 bash 进程

Send a Message