Z(zombie)-僵尸进程(子进程终止,父进程没有wait子进程)

僵尸即“死了还活着”,子进程已经终止了,也就是说子进程死了。但是系统会保留子进程的信息(进程ID/终止状态/使用的CPU时间总量),这些残余信息仍然存在,也就是说某种意义上还活着。

孤儿则是父进程终止了,也就是丧父。

  • 僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。(一个进程结束了,但是他的父进程没有等待(调用wait/ waitpid)他,那么他将变成一个僵尸进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  • 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程。因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程,看看有没有哪个 进程是刚刚结束的这个进程的子进程,如果是的话,就由Init进程来接管他,成为他的父进程,从而保证每个进程都会有一个父进程。而Init进程会自动 wait其子进程,因此被Init接管的所有进程都不会变成僵尸进程。

为了观察到僵尸进程,我们自己写一个不正常的程序,父进程 fork 出子进程,子进程终止,而父进程既不终止也不调用 wait 清理子进程:

   #include<stdio.h>
   #include<unistd.h>
   #include<stdlib.h>
   
   int main()
   {
     int ret = fork();
     int i = 100;
     if(ret < 0)                                                                      
     {
       perror("fork error !");
       return 1;
     }
     else if(ret == 0){
       //child
      printf("i am child. my pid is : %d, my ppid is : %d.\n", getpid(), getppid());
    }
    else{
      //father
     printf("i am father. my pid is %d.\n", getpid());
     for(; i > 0; i--)
     {
      sleep(1);
     }
    }
     return 0;
  }            

僵尸进程的详细讲解_#include
此时子进程已经退出,但是父进程没有退出也没有通过 wait() 调用处理子进程。我们使用 ps 命令查看进程的状态:

僵尸进程的详细讲解_进程退出_02

上图红框中的大写字母 “Z” 说明 PID 为 46143(子进程)的进程此时处于僵死的状态。

系统为什么需要僵尸进程这种进程状态

由于父进程创建子进程是异步的,双方不知道各自的运行状态,而父进程有的时候需要知道子进程退出时的一些信息,所以 linux提供了一种机制,通过让子进程退出时向父进程发送 SIGCHRD 信号来告知父进程,子进程已经退出了。同时,父进程通过调用 wait 和 waitpid 来获取子进程的退出信息。

僵尸进程的危害

  • 占用系统资源
  • 造成内存泄漏

如何防止僵尸进程

  • 让僵尸进程变成孤儿进程,由init回收,就是让父亲先死
  • 采用信号SIGCHLD通知处理,并在信号处理程序中调用wait函数
  • 让僵尸进程的父进程来回收,父进程每隔一段时间来查询子进程是否结束并回收,调用wait()或者waitpid(),通知内核释放僵尸进程
    僵尸进程的详细讲解_父进程_03僵尸进程的详细讲解_父进程_04
    僵尸进程的详细讲解_进程退出_05