解析僵尸进程与孤儿进程

来源:互联网 发布:逆战磁暴矩阵 编辑:程序博客网 时间:2024/05/22 08:21

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

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

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

僵尸进程

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

孤儿进程

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

二、解决方法

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

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

(1)用信号进行处理

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

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

思路:

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

方法:

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

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

(一)孤儿进程

#include <unistd.h>#include <stdlib.h>#include <stdio.h>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;}

这里写图片描述

(二)僵尸进程

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

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

#include <unistd.h>#include <stdlib.h>#include <stdio.h>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;}

这里写图片描述

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

(1)用信号进行处理

#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <errno.h>#include <signal.h>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);}

这里写图片描述

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

#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>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;}

这里写图片描述

原创粉丝点击