【Linux】剖析僵尸进程和孤儿进程

【Linux】剖析僵尸进程和孤儿进程

  在操作系统内核中,具体的进程状态有:R(运行状态)、S(可中断睡眠状态)、D(磁盘睡眠状态,不可被打断)、T(暂停状态)、t(跟踪状态)、X(死亡状态)、Z(僵尸状态)。那么接下来详细讲解一下僵尸进程(Zombie),它是一个比较特殊的进程状态。


文章目录

  • 【Linux】剖析僵尸进程和孤儿进程
    • 一、僵尸进程
      • 1.1 产生僵尸进程的原因
      • 1.2 僵尸进程的危害
      • 1.3 如何预防僵尸进程的产生
    • 二、孤儿进程
      • 2.1 产生孤儿进程的原因
      • 2.2 现象
      • 2.3 注意


一、僵尸进程

  我们先来看一段代码。

#include
#include

int main()
{
    printf("---begin---\n");
    //进程创建
    pid_t ret = fork();
    if(ret < 0)
    {
        perror("fork error\n");
        return 0;
    }
    else if(ret == 0)
    {
        //子进程
            printf("i am child pid=[%d] ppid[%d]\n",getpid(),getppid());
            sleep(20);
    }
    else
    {
        //父进程
        while(1)
        {
            printf("i am father pid=[%d] ppid[%d]\n",getpid(),getppid());
            sleep(1);
        }
    }
    return 0;
}

  运行: 可以看到父进程的 PID 是 10377,子进程的 PID 是 10378。

【Linux】剖析僵尸进程和孤儿进程_第1张图片

  分析代码: 通过可以知道,父进程一直在 while 循环,子进程 sleep 个 20 秒会退出。用 ps aux | grep [程序名称] 查看进程信息,可以看到子进程从刚开始的 S(可中断睡眠模式)变成 Z(僵尸状态)。这是为什么呢?

【Linux】剖析僵尸进程和孤儿进程_第2张图片


1.1 产生僵尸进程的原因

  子进程先于父进程退出,父进程来不及回收子进程的的资源,导致子进程变成僵尸进程。而此时子进程的 PCB ( task_struct) 还在操作系统内核中被双向链表管理着,但对于子进程而言,程序已经退出,不能执行任何用户写的代码,子进程的状态会变成 Z 状态,刀枪不入。

  僵尸进程对应的进程状态是 Z 状态,并且使用 kill 命令或者 kell -9 都结束不了僵尸进程。

【Linux】剖析僵尸进程和孤儿进程_第3张图片

  补充: 使用 pstack [PID] 命令可以查看程序的调用堆栈情况


1.2 僵尸进程的危害

  如果产生僵尸进程之后,僵尸进程是不能被 kill -9 强杀杀死的 (因为程序已经退出了)。也就意味着操作系统内核用双向链表还管理着僵尸进程的 PCB (task_struct),操作系统内核维护这样的结构体是需要消耗内存的,程序员还结束不了僵尸进程,操作系统就会内存泄漏。


1.3 如何预防僵尸进程的产生

  僵尸进程产生的本质原因是因为子进程先于父进程退出,抓住问题的本质,来看看该如何预防僵尸进程的产生。

  1、让子进程不要先于父进程退出。~呃呃呃 ~ 呃~ 这个好像可能性不大,不可取。

  2、进程等待。~ 呃 ~ 这个可取,优雅的预防。详细内容参考这篇博文

  3、将父进程 kill 掉,让僵尸进程成孤儿进程被1号进程所领养,1号进程回收僵尸进程的资源。~ 呃呃 ~ 这个还想不太可取,有点杀敌一千自损八百的感觉。


二、孤儿进程

2.1 产生孤儿进程的原因

  父进程先于子进程退出,子进程会被1号进程所领养(1号进程又被称之为 init 进程)。1号进程会在子进程退出的时候,回收子进程的退出信息,防止子进程变成僵尸进程。


2.2 现象

  我们再来看这段代码。

#include
#include

int main()
{
    printf("---begin---\n");
    //进程创建
    pid_t ret = fork();
    if(ret < 0)
    {
        perror("fork error\n");
        return 0;
    }
    else if(ret == 0)
    {
        //子进程
            printf("i amchild pid=[%d] ppid[%d]\n",getpid(),getppid());
            sleep(20);
    }
    else
    {
        //父进程
            printf("i am father pid=[%d] ppid[%d]\n",getpid(),getppid());
            sleep(5);
    }
    return 0;
}

  运行: 可以看到父进程的 PID 是 19069,子进程的 PID 是 19070。

【Linux】剖析僵尸进程和孤儿进程_第4张图片

  分析代码: 通过可以知道,父进程 sleep 个5秒会退出,子进程 sleep 个20 秒然后退出,也就是说父进程先于子进程退出。用 ps -ef | grep [程序名称] 查看进程PID,可以看到 5 秒后父进程退出, 随后子进程的 PPID 从刚开始 19069 变为1,也就是说父进程先于子进程退出,子进程被 1 号进程所领养,1 号进程在子进程退出时回收退出信息。
【Linux】剖析僵尸进程和孤儿进程_第5张图片


2.3 注意

  孤儿进程不是一种进程状态!!!而是一种进程种类的名称。

   一个进程的进程状态只有 R、S、D、T、t、Z、X。


你可能感兴趣的:(Linux,进程,孤儿进程,僵尸进程,ps,aux,ps,-ef)