僵尸进程和孤儿进程简介

Posted on 2016-11-30 in Linux

前言

最近在做内部使用的 Docker base image,老大甩给我几个链接让我关注一下 Docker init process 的问题,其中有一篇文章详细描述了 Docker PID 1 僵尸程序回收的问题,我也趁此机会学习了一下有关僵尸进程和孤儿进程的知识。

基本概念

Linux 系统中运行的所有进程按照一颗树的形式组织。每个进程都可以生成子进程,并且除了最顶端的init进程,每个进程都会有一个父进程。

当我们启动系统的时候,kernel会启动最顶端的init进程,然后由init进程启动余下的部分,比如说SSH服务,Docker服务,Apache/Nginx,图形化界面等等,而它们又可以继续生成其他的子进程。

Unix process hierarchy

通常来说,当一个子进程退出的时候,父进程会收到操作系统发来的SIGCHLD信号并且调用wait()或者waitpid()来取得子进程的终止状态,这个行为被叫做“回收(reaping)”。

Child process reaping

但凡事总有例外。

比如说父进程在子进程退出之后并没有调用waitpid(),又或者子进程仍在运行,而出于某些原因父进程退出了或者被终止了,这时候会出现什么情况?

  • 孤儿进程(Orphan process):一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(PID 1)收养,并由init进程对它们完成状态收集工作。
  • 僵尸进程(Zombie process):一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait()或者waitpid()获取子进程的状态信息,那么子进程的进程描述符仍然保留再系统中。这种进程称之为僵尸进程。

危害

对于孤儿进程,由于它会被init进程接管并处理后续工作,因此并不会有什么危害。

而对于僵尸进程,参考Linux waitpid man page:

"As long as a zombie is not removed from the system via a wait, it will consume a slot in the kernel process table, and if this table fills, it will not be possible to create further processes."

也就是说,如果父进程不调用 wait/waitpid 的话,即使子进程所占用的内存空间已经被释放,它在进程表中保留的信息却没有释放,其进程号会一直被占用,但是系统所能使用的进程号是有限的,如果产生大量的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。

参考资料

Docker and the PID 1 zombie reaping problem
孤儿进程与僵尸进程[总结]