僵尸进程与孤儿进程

  在unix/linux系统中,大多情况下,子进程是通过父进程fork创建的,注:系统调用fork,是一个比较有意思系统调用,它调用一次,返回两个值,失败返回-1,成功时在子进程返回0,父进程返回所创建子进程的pid。

  子进程创建后,子进程的结束和父进程的运行是一个异步过程,也就是说父进程没办法预测子进程什么时候结束。 当一个子进程完成它的工作终止之后,其父进程需要调用wait()或waitpid()去获取子进程的终止状态。

  孤儿进程

  所谓孤儿进程,顾名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。最后孤儿进程将会被init进程(进程号为1)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害。

  下面看一个关于孤儿进程的例子:在main函数中,创建子进程,然后让父进程睡眠1s,让子进程先运行打印出其进程id(pid)以及父进程id(ppid);随后子进程睡眠3s(此时会调度到父进程运行直至结束),目的是让父进程先于子进程结束,让子进程有个孤儿的状态;最后子进程再打印出其进程id(pid)以及父进程id(ppid);观察两次打印 其父进程id(ppid)的区别。

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

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();

    if (pid < 0){
        perror("fork failed");
        exit(1);
    }
    if (pid == 0){
        printf("i am the child process\n");
        printf("pid:%d\t ppid:%d\n", getpid(),getppid()); //输出进程ID和父进程ID
        printf("i will sleep five seconds\n");
        
        // 子进程休眠3秒,保证父进程先退出
        sleep(3);
        printf("now pid:%d\t ppid:%d\n", getpid(),getppid());
        printf("child process is exited\n");
    }else{//父进程
        printf("i am father process\n");

        //保证子进程先运行
        sleep(1);
        printf("father process is exited\n");
    }
    return 0;
}

  输出结果:

/*
i am father process
father process is exited
i am the child process
pid:45290     ppid:45289
i will sleep five seconds
now pid:45290     ppid:1
child process is exited    
*/

  从运行结果来看:当其父进程结束后,子进程成为了孤儿进程,其父进程id(ppid)为1,也就是说,init进程成为该子进程的父进程了。

  僵尸进程

  僵尸进程是指:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的某些信息如进程描述符仍然保存在系统中。这种进程称之为僵死进程。

  下面是1个关于僵尸进程的例子:在main函数中,创建子进程,然后让父进程睡眠10s,让子进程先终止(注意和孤儿进程例子的区别);这里子进程结束后父进程没有调用wait/waitpid函数获取其状态,用ps查看进程状态可以看出子进程为僵尸状态。

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

int main(int argc, char const *argv[])
{
    pid_t pid;
    pid = fork();


    if (pid < 0){
        perror("fork failed");
        exit(1);
    }
    if (pid == 0){
        printf("i am child process,i am exited\n");
        exit(0);
    }

    printf("i am father process\n");

    sleep(10); // 保证子进程结束

    printf("father process is exited\n");

    return 0;
}

  此时程序变为了僵尸进程。

  注:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。  如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

  僵尸进程的危害:僵尸进程会在系统中保留其某些信息如进程描述符、进程id等等。以进程id为例,系统中可用的进程id是有限的,如果由于系统中大量的僵尸进程占用进程id,就会导致因为没有可用的进程id系统不能产生新的进程,这种问题可就大了,这就是僵尸进程带来的危害。

 

posted @ 2018-03-07 10:21  看雪。  阅读(478)  评论(0编辑  收藏  举报