关于我 首页

进程拾遗:孤儿进程和僵尸进程

2019-06-17

其实这两个概念并不难理解,只要明白了以下两个关键点就可以了:

孤儿进程

明白了父子进程的关系,那么有一个问题:如果父进程先于子进程终止,子进程的资源岂不是没人来释放了? 对于这种父进程比自身先终止的进程,顾名思义称之为孤儿进程

对于孤儿进程,内核自然不会放任不管,内核会将init进程设置为孤儿进程的父进程,美其名曰收养,从而当此进程退出后由init来回收其资源。

可以写一个小程序来验证一下:

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

int main() {
    pid_t pid = fork();

    if( pid > 0 ){
        //父进程就退出了
        printf("我是父进程,我的进程号是:%d\n",getpid());
        sleep(1);
    }else{
        printf("我是子进程,我的父进程是%d\n",getppid());
        sleep(2);
        printf("我是子进程,父进程终止后,我的父进程是:%d\n",getppid());
    }
    return 0;
}

在这个小程序中,对于父进程先获取其进程号,为:11552,然后让其休眠1秒。对于子进程,先获取父进程号,此时父进程还未终止,所以取到的父进程号也为:11552。然后让其休眠两秒,2秒过后,父进程已经终止了,再取其父进程号,发现已经是1号进程,也就是init了。

僵尸进程

我们考虑父子进程的另一种情况:如果父进程还未调用wait族函数来等待并回收子进程资源,子进程就已经终止了,那么子进程的资源会被如何回收?

我们可能会想,内核应该会自动回收……吧?确实,内核会回收该进程的大部分资源,但是内核会在进程表中保留这一条进程记录,它的ID,它的终止状态等信息。为什么要这么做,而不是全部回收呢,因为内核要保留这些信息,以便父进程调用wait函数时能告知子进程已经终止和终止原因。

也就说是,虽然子进程提前终止了,但父进程还可以在终止后通过调用wait族函数来获取子进程终止原因。

这种已经终止但仍然还保留有一条进程记录的进程,就称谓僵尸进程。为什么叫僵尸呢,因为为了保留这条记录以便父进程获知子进程终止状态,这类进程无论如何都杀不死,即使使用的是KILL -9 进程号也就是发送SIGKILL信号也不行。

那么我们考虑,如何才能杀死僵尸进程呢? 也很简单:

也写个小程序验证一下:


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

int main() {
    pid_t pid = fork();
    if( pid > 0 ){
        printf("我是父进程,进程号是:%d\n",getpid());
        //父进程什么都不做,一直在休眠
        sleep(100000);
    }else{
        printf("我是子进程,进程号是:%d\n",getpid());
    }
    return 0;
}

运行这个程序,会打印出父子进程号,然后父进程就休眠了,而子进程无事可做就终止了。这时候如果通过ps命令查看子进程的状态,就能看到子进程处于Z+,也就是僵尸状态了。而此时如果杀掉父进程,会发现子进程也不存在了,因为子进程被init回收了。

试试杀掉子进程?没用的,不然怎么叫僵尸呢!