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 查看信号编号的对应内容。

说说你的想法