wait() 回收子进程

在前面的文章中我们讨论了如何通过 fork() 函数创建子进程,创建后的子进程如果优先于父进程退出,子进程的虚拟内存空间就消失了,但是进程控制块PCB并没有消失,这里面包含了这个子进程的退出状态,需要由父进程来进行回收。在父进程回收之前,这个进程被称为僵尸进程(僵死进程),任何一个子进程都会经过这段僵尸进程的阶段,最后由父进程来回收。
子进程退出有两种情况,一种是程序正常退出了,比如exit(1)或者main函数返回等。而另外一种则是非正常退出,一般情况下是收到了某种信号,比如“kill -9”。正常退出的情况下,我们要获取进程退出的代码,而非正常退出的状态下我们要获取到底进程因为哪中信号而终止了。

我们首先来看一个最简单的 wait 函数的应用:

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

int main(int argc, char* argv[])
{
    pid_t pid, wpid;

    pid = fork();

    if (pid  == -1)
    {
        perror("fork error");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I'm child, PID = %d\n", getpid());
        // 子进程等待5秒
        sleep(5);
    }
    else if (pid > 0)
    {
        // 父进程调用wait函数处于阻塞状态,直到子进程结束wait才会返回
        wpid = wait(NULL);
        if (wpid == -1)
        {
            perror("wait error");
        }
        printf("I'm parent, I catched child process, PID = %d\n", wpid);
    }
    return 0;
}

上面代码是一个最简单的 wait() 的应用了,这样的 wait() 只起到了回收子进程 PCB 的作用,而并没有对子进程的退出消息做任何的接收或处理。下面的代码演示了如何对子进程的退出情况做出响应。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char* argv[])
{
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        perror("fork error");
        exit(1);
    }
    else if (pid == 0)
    {
        printf("I'm child, PID = %d\n", getpid());
        sleep(1);
    }
    else if (pid > 0)
    {
        int status;
        int wpid;
        wpid = wait(&status);
        // 子进程正常退出判断,如果返回真则证明子进程是正常退出的
        if (WIFEXITED(status))
        {
            // 打印已经退出的子进程的 pid
            printf("I'm parent, The child process %d exit normally\n", wpid);
            // 打印子进程程序退出后的返回值
            printf("return value : %d\n", WEXITSTATUS(status));
        }
        // 如果子进程是非正常退出,那么一定是接收到某信号,下面就是判断是否是接收到了信号
        else if (WIFSIGNALED(status))
        {
            // 获取进程是接收到了什么信号导致非正常退出的
            printf("Child terminated abnormally, signal %d\n", WTERMSIG(status));
        }
    }
    return 0;
}

从上面的例子中可以看出,WIFEXITED是用来判断子进程的退出状态的,如果返回真则证明子进程是正常退出的,随后使用了WEXITSTATUS获取了退出代码。而WIFSIGNALED则是判断子进程是否是接收到了某个信号,如果是收到了某个信号证明进程是非正常退出的,那么使用WTERMSIG获取收到的信号。可通过 kill -l 查看信号编号的对应内容。

发表评论