Linux应用编程 | wait及waitpid

wait函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

① 阻塞等待子进程退出

② 回收子进程残留资源

③ 获取子进程结束状态(退出原因)。

    pid_t wait(int *status); 成功:清理掉的子进程ID;失败:-1 (没有子进程),同时errno设为ECHILD

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)

可使用wait函数传出参数status来保存进程的退出状态。如果我们对于子进程的退出状态并不在意的话,只是想回收该进程的资源,那直接往status传NULL即可(实际上我们大部分情况都不在意子进程退出状态)。如果想知道子进程退出状态的话,可借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

 1.  WIFEXITED(status) 为非0     → 进程正常结束

         WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)

 2. WIFSIGNALED(status) 为非0 → 进程异常终止

         WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

*3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态

         WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

         WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

 

例程1:

调用wait死等回收子进程,不关心子进程退出状态(status参数传NULL)。

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

int main()
{
	pid_t pid, wpid;
	
	pid = fork();
	if (pid == -1) {
		perror("fork");
		exit(1);
	} else if (pid == 0) {
		/* 子进程 */
		printf("I'm child process, pid = %d, going to exit in 3s\n", getpid());
		sleep(3);
	} else if (pid > 0) {
		/* 父进程 */
		printf("I'm parent process, going to wait the child process\n");
		/* 调用wait函数后,父进程将在此死等,直到子进程退出 */
		wpid = wait(NULL);	//如果不关心子进程的退出状态,只是想将子进程回收,那么status参数传NULL即可
		printf("I'm parent process, I catched child process: %d\n", wpid);
	}
	return 0;
}

例程2:

此例程我们用NORMALEXIT这个宏来控制子进程是否正常退出。如果NORMALEXIT为0的话,子进程通过execl调用一个abnormal函数,该函数将做除0操作,导致异常。

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

#define NORMALEXIT 0

int main()
{
	pid_t pid, wpid;
	int status;
	
	pid = fork();
	if (pid == -1) {
		perror("fork");
		exit(1);
	} else if (pid == 0) {
		/* 子进程 */
#if NORMALEXIT
		/* 子进程正常退出的情况 */
		printf("I'm child process, pid = %d, going to exit in 3s\n", getpid());
		sleep(3);
		exit(10);
#else
		/* 子进程异常退出的情况 */
		execl("./abnormal", "abnormal", NULL);
		perror("execl");
		exit(11);
#endif
	} else if (pid > 0) {
		/* 父进程 */
		printf("I'm parent process, going to wait the child process\n");
		wpid = wait(&status);	//使用status接收子进程的退出状态
		if (WIFEXITED(status)) {	//如果子进程正常退出,则WIFEXITED宏函数返回真
			printf("I'm parent process, child process: %d exit normally with return value: %d\n", 
				wpid, WEXITSTATUS(status));		//使用WEXITSTATUS获取进程退出状态(exit的参数)
		} else if (WIFSIGNALED(status)) {
			printf("I'm parent process, child process: %d is killed by signal: %d\n", 
				wpid, WTERMSIG(status));	//使用WTERMSIG获取使子进程终止的那个信号的编号
		}

	}
	return 0;
}


//abnormal.c
int main()
{
	int a = 1/0;
	return 22;
}

当子程序正常退出时:

    父进程抓到了子进程的退出参数:10。

当子程序异常退出时:

    父进程抓到了杀死子进程的信号:8(除零出错)。

 

waitpid函数

作用同wait,但可指定pid进程清理,可以不阻塞。

    pid_t waitpid(pid_t pid, int *status, in options);  成功:返回清理掉的子进程ID;失败:-1(无子进程)

特殊参数和返回情况:

参数pid:

> 0 回收指定ID的子进程  

-1 回收任意子进程(相当于wait)

0 回收和当前调用waitpid一个组的所有子进程

< -1 回收指定进程组内的任意子进程

返回0:参3为WNOHANG,且子进程正在运行。

参数options:目前只提供了两个选项:WNOHANG和WUNTRACED。如果不想使用的话,把它设为0即可。如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,用得极少。 

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

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

int main()
{
	pid_t pid, wpid;
	
	pid = fork();
	if (pid == -1) {
		perror("fork");
		exit(1);
	} else if (pid == 0) {
		/* 子进程 */
		printf("I'm child process, pid = %d, going to exit in 3s\n", getpid());
		sleep(3);
	} else if (pid > 0) {
		/* 父进程 */
		printf("I'm parent process, going to wait the child process\n");
		do {
			wpid = waitpid(pid, NULL, WNOHANG);	//使用WNOHANG参数,不会阻塞,直接往下运行
			if (wpid ==0) {
				printf("no child process exit, wait 1s and try again\n");
				sleep(1);	//没有子进程退出,等待1秒再检测
			}
		} while (wpid == 0);
		
		if (wpid == pid) {
			printf("I'm parent process, I catched child process: %d\n", wpid);
		} else {
			printf("other situation...\n");
		}
	}
	return 0;
}

 

---------------

码字不易,点个赞再走呗~

我是良许,世界500强外企 Linux 开发工程师,专业生产 Linux 干货。欢迎关注我的公众号「良许Linux」,回复「1024」获取最新最全的技术资料,回复「入群」进入高手如云技术交流群;提供永久免费 CSDN 资料下载服务。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良许Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值