目录

Linux系统中僵尸进程的二三事

介绍什么是僵尸进程,以及如何处理与预防僵尸进程的产生


僵尸进程产生的原因

ps 查看进程状态时,僵尸进程的 STAT 栏为 defunct,该进程早已死亡,但在进程表仍占用了一个 slot。由于进程表的容量是有限的,所以,defunct 进程不仅占用系统的内存资源,影响系统的性能,而且如果其数目太多,还会导致系统瘫痪。

子进程先于父进程退出,比如子进程内执行了 exit 命令(exit 的作用是使进程退出,并不能将其完全销毁)。此时进程表中的数据会被该进程的退出码(exit code)、执行时所用的 CPU 时间等数据所取代,这些数据会一直保留到系统将它传递给它的父进程为止。

僵尸进程放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出信息供其他进程收集,此外僵尸进程不占用任何存储空间。

如何杀死僵尸进程

defunct 状态下的僵尸进程不能直接使用 kill -9 命令杀死。

普通进程

杀死父进程,此时僵尸进程会变成孤儿进程,由 pid=1 的 init 进程会扫描被杀死进程下的子进程,并且把僵尸进程进行回收。

使用 ps -ef | grep defunct_process_id 命令可以找到 defunct 的僵尸进程的父进程。

ppid=1 的进程

init 会时刻扫描子进程的状态,那么会在什么情况下,产生 ppid 为 1 的僵尸进程呢?

  1. 进程还在被其他进程使用,但是已退出
  2. 进程的子进程还在执行任务,但是父进程已经死亡
  3. 进程阻塞在某次 I/O 请求上,此时控制权交到了内核,如果该进程被 kill,那么就会成为 ppid=1 的僵尸进程,这个进程不会退出,会一直阻塞直到 I/O 请求被满足。

因此,当出现 ppid=1 的僵尸进程时,可以从以下情况分析:

  1. 查看当前僵尸进程是否被其他进程调用,如被跟踪、调试等
  2. ps -T -p查看下这个僵尸进程的主线程是否退出
  3. strace跟踪下这个僵尸进程看看是否有 io 在等待,或者查看下载僵尸之前是否有 io 类的操作发生

使用 ps -ef | grep default_process_id 搜出 gdb attach defunct_process_id,kill 掉 gdb attach 后,僵尸进程就被回收了。

如何预防僵尸进程

  • 在父进程创建子进程之前,就向系统申明自己并不会对这个子进程的 exit 动作进行任何关注行为,这样的话,子进程一旦退出后,系统就不会去等待父进程的操作,而是直接将该子进程的资源回收掉,也就不会出现僵尸进程了。具体的办法就是,在父进程的初始化函数中,调用这个函数:signal(SIGCHLD,SIG_IGN)
  • 如果上述语句没来得及调用,也有另外一个办法。那就是在创建完子进程后,用waitpid()等待子进程返回,也能达到上述效果
  • 如果上述两个办法都不愿意采用,那还有一招:在父进程创建子进程的时候,连续调用两次fork(),而且使紧跟的子进程直接退出,使其孙子进程成为孤儿进程,从而 init 进程将代替父进程来接手,负责清除这个孤儿进程。于是,父进程就无需进行任何的清理行为,系统会自动处理