Linux中僵尸状态和孤儿状态详细分析

先上代码:

僵尸状态&僵尸进程

#include <stdio.h>
#include <unistd.h>

int main()
{
    
    
    printf("hello\n");
    pid_t ret = fork();
    if(ret < 0)
    {
    
    
        perror("fork");
        return 0;
    }
    else if(ret == 0)
    {
    
    
        //child
        while(1)
        {
    
    
            printf("i am child, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    else
    {
    
    
    	//father
        while(1)
        {
    
    
            printf("i am father, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    return 0;
}

输出结果为:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200806095501337.png
由上图可知pid为5577的为子进程,pid为5576的为父进程,pid为5576的为父进程的父进程。
在这里插入图片描述
1. 创建子进程的代码,使用pstack命令可以查看程序的调用堆栈,注意pstack获取的是程序一瞬间的代码,或许下一个就不会再执行当前代码。
举例:pstack 5577,查看pid为5577进程的调用情况,由下图可知代码在19行调用,对应子进程。
在这里插入图片描述
pstack 5576,查看pid为5576进程的调用情况,右下图可知代码在28行调用,对应父进程。
在这里插入图片描述
2. 僵尸状态
修改代码如下:

#include <stdio.h>
#include <unistd.h>

int main()
{
    
    
    printf("hello\n");
    pid_t ret = fork();
    if(ret < 0)
    {
    
    
        perror("fork");
        return 0;
    }
    else if(ret == 0)
    {
    
    
        //child
        //while(1)
        {
    
    
            printf("i am child, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(20);
        }
    }
    else
    {
    
    
    	//father
        while(1)
        {
    
    
            printf("i am father, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    return 0;
}

运行结果:
在这里插入图片描述
ps aux | grep ./test,查看当前进程状态。由下图可以看到,当前进程中只有一个父进程。
在这里插入图片描述
ps aux | grep test,再次查看当前进程状态。右下图可以看到,当前进程中有父进程和子进程。
在这里插入图片描述

原理:子进程先于父进程退出,父进程来不及回收子进程的资源,导致子进程变成僵尸进程,而子进程对应的task_struct还在内核当中,双向链表管理着,对于子进程而言,程序员已经退出了,并不能任何执行任何用户写的代码了;子进程的状态会变成Z状态:刀枪不入!!!

3. 产生僵尸进程之后,僵尸进程对应的状态是Z,并且使用kill命令或者强杀命令kill -9,都结束不了僵尸进程。
4. 僵尸进程的危害:
  产生僵尸进程之后,僵尸进程是不能被强杀杀死的,原因是僵尸进程已经死了,意味着在操作系统内核双向链表还维护着僵尸进程的task_struct。内核维护这样的结构体,是需要消耗内存的;程序员还结束不了僵尸进程,操作系统就会产生内存泄漏。
5. 如何预防僵尸进程的产生

  • 子进程不要先于父进程退出,但可能性并不是很大 ---- 不可取的。
  • 进程等待,在后续博客再详细说明 ---- 可取,较为优雅。
  • 可以将父进程kill掉,该进程就变成了孤儿进程,然后让僵尸进程被1号进程领养,1号进程回收僵尸级进程的资源(杀敌一千自损800 ---- 不可取)

孤儿状态

1. 原理:
  父进程先于子进程退出,子进程会被1号进程所领养,1号进程被称为init进程。1号进程会在子进程退出时,回收子进程的退出信息,防止子进程变成僵尸进程。
  ps aux | grep 1,查看1号进程,1号进程是Linux操作系统当中启动的第一个进程,该进程会启动若干个独立的子进程。
在这里插入图片描述
2. 现象
继续修改代码如下,修改后父进程先退出,子进程后退出:

#include <stdio.h>
#include <unistd.h>

int main()
{
    
    
    printf("hello\n");
    pid_t ret = fork();
    if(ret < 0)
    {
    
    
        perror("fork");
        return 0;
    }
    else if(ret == 0)
    {
    
    
        //child
        //while(1)
        {
    
    
            printf("i am child, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(20);
        }
    }
    else
    {
    
    
    	//father
        //while(1)
        {
    
    
            printf("i am father, pid=%d, ppid=%d\n", getpid(), getppid());
            sleep(5);
        }
    }
    return 0;

ps -ef | grep test,查看子进程和父进程的pid和ppid,第一次查看如下:
在这里插入图片描述
第二列是pid,第三列是ppid,可知9041是当前进程,9040是当前进程的父进程,4289是当前进程中父进程的父进程也就是终端bash。下面是第二次查看:
在这里插入图片描述
由于父进程提前退出,所以子进程会被1号进程领养。因此当前进程的父进程变成了1号进程。我们称这样的进程为“孤儿进程”。

3. 注意:孤儿进程不是一种状态!!!而是一种进程种类的名称。

4. 子进程和父进程代码共享,数据独有!
修改代码如下:

#include <stdio.h>
#include <unistd.h>

int g_val = 100;

int main()
{
    
    
    printf("hello~\n");
    pid_t ret = fork();
    if(ret < 0)
    {
    
    
        perror("fork");
        return 0;
    }
    else if(ret == 0)
    {
    
    
        //child
        //while(1)
        {
    
    
            printf("i am child pid=[%d], ppid=[%d]\n", getpid(), getppid());
            g_val -= 50; //50
            printf("child, g_val:%d\n", g_val);
            sleep(20);
        }
    }
    else
    {
    
    
        //ret > 0 : father
        while(1)
        {
    
    
            printf("i am father pid=[%d], ppid=[%d]\n", getpid(), getppid());
            g_val += 50; // 150
            printf("father, g_val:%d\n", g_val);
            sleep(1);
        }
    }
    return 0;
}

在这里插入图片描述
由上图结果能看出来,父进程和子进程结果相互独立,互补干扰。

猜你喜欢

转载自blog.csdn.net/aaaaauaan/article/details/107832201