解析僵尸进程与孤儿进程

一、僵尸进程与孤儿进程的定义

我们知道一个子进程如果要结束,其内核释放进程的所有资源,但是还保存了一部分资源供父进程使用(是一个被称之为僵尸进程的数据结构,包含有进程号、运行时间、退出状态等,它需要父进程去处理),所以其父进程要调用wait或者waitpid获取子进程的状态信息,然而如果父进程没有调用这个信息会发生什么情况呢?

  • 如果父进程没有调用这个信息,那么这段信息将会被一直占用,但是系统中的资源是有限的,如果大量资源被占用,此时将无法产生新的信息。

僵尸进程

一个进程如果创建出子进程,如果此时子进程退出,而父进程没有进行善后工作(wait与waitpid获取子进程状态信息),那么此时子进程的进程描述符仍然保存在系统中。

孤儿进程

如果一个父进程退出,它的子进程(有一个或者多个)还在,那么子进程将成为孤儿进程,这个时候这些孤儿进程会被1号进程,也就是init进程所收养,并且由init进程完成善后工作(状态收集)。

二、解决方法

由于孤儿进程在最后都会被一个一号进程也就是init进程所收养,而init进程会对这个孤儿进程进行善后工作,所以一般情况下我们不需要处理孤儿进程。

但是僵尸进程如果不处理会占用大量资源,所以我们必须要对僵尸进程进行处理,一般的处理方式我们有如下两种:

(1)用信号进行处理

信号处理是一般的做法,一般子进程退出的时候向父进程发送SIGCHILD信号,所以我们只需要捕捉这个信号,在信号处理函数中进行wait(),对其子进程进行善后工作,将不会产生僵尸进程。

(2)fork()两次进行处理

思路:

如果父进程处理时间长,子进程处理时间短,如果父进程不wait()处理的话,子进程就会产生僵尸进程,但是如果父进程进行了wait()处理,那么父进程又会产生阻塞,所以解决方法就是让自己尽快退出,任务让子进程的子进程来处理。

方法:

fork()两次的做法是在unix环境高级编程一书中所提到的,其方法是在fork第一次进程的里面再用一次fork,然后让第一次fork出来的子进程变为孤儿进程,交给init进程进行处理,自己则用waitpid善后即可。

三、僵尸进程与孤儿进程的实现

(一)孤儿进程

#include 
#include 
#include 
int main()
{
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        exit(1);
    }
    //子进程
    if (pid == 0)
    {
        printf("child\n");
        printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
        //保证父进程退出
        sleep(5);
        printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
    }
    else
    {
        printf("I am father\n");
        sleep(1);
        printf("father exit!\n");
    }
    return 0;
}

解析僵尸进程与孤儿进程_第1张图片

(二)僵尸进程

提供了如下两种僵尸进程的方式:

①子进程退出,父进程不wait

#include 
#include 
#include 
int main()
{
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork error");
        exit(1);
    }
    //子进程
    if (pid == 0)
    {
        printf("child\n");
        printf("My Pid:%d,My Father pid:%d\n", getpid(), getppid());
        printf("I exit!\n");
        exit(0);
    }
    printf("I am father\n");
    //父进程等待1秒不处理子进程
    sleep(1);
    //打印进程信息
    system("ps -o pid,ppid,state,tty,command");
    printf("father exit!\n");
    return 0;
}

解析僵尸进程与孤儿进程_第2张图片

四、僵尸进程与孤儿进程的解决方法实现

(1)用信号进行处理

#include 
#include 
#include 
#include 
#include 

static void sig_child(int signo);
int main()
{
    pid_t pid;
    signal(SIGCHLD, sig_child);
    pid = fork();
    if (pid < 0)
    {
        perror("Fork error");
        exit(1);
    }
    if (pid == 0)
    {
        printf("I am child %d!\n", getpid());
        exit(1);
    }
    printf("I an father! \n");
    sleep(3);
    system("ps -o pid,ppid,state,tty,command");
    printf("father exit!\n");
    return 0;
}

static void sig_child(int signo)
{
    pid_t pid;
    int stat;
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated.\n", pid);
}

解析僵尸进程与孤儿进程_第3张图片

(2)fork()两次进行处理

#include 
#include 
#include 
#include 

int main()
{
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("Fork error!\n");
        exit(1);
    }
    if (pid == 0)
    {
        printf("The first pid:%d,ppid:%d\n", getpid(), getppid());
        //此时fork第二次
        pid = fork();

        if (pid < 0)
        {
            perror("Fork error!\n");
            exit(1);
        }
        else if (pid > 0)
        {
            //第一次创建的进程退出
            printf("first exit!\n");
            exit(0);
        }
        if (pid == 0)
        {
            //子进程休息三秒保证第一次fork出的进程退出,此时自己就变为孤儿进程了。
            printf("I an second,pid:%d,ppid:%d\n", getpid(), getppid());
            sleep(3);
            exit(0);
        }
    }
    //这里是处理第一个进程
    if (waitpid(pid, NULL, 0) != pid)
    {
        perror("waitpid error!\n");
        exit(1);
    }
    exit(0);
    return 0;
}

这里写图片描述

你可能感兴趣的:(Linux)