linux wait、waitpid函数回收子进程

wait函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中使用echo $?命令查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。

pid_t wait(int *status); //注意,int型的status变量需要外部创建,然后传入地址

成功:返回清理掉的子进程ID;失败:返回-1 (没有子进程)
当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)
可使用wait函数的传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

  1. WIFEXITED(status) 为非0 → 进程正常结束
    WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
  2. WIFSIGNALED(status) 为非0 → 进程异常终止
    WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
  3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态
    WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
    WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

例1:
wait1.c

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

int main(void)
{
	pid_t pid, wpid;

	pid = fork();

	if(pid == -1){
		perror("fork error");
		exit(1);
	} else if(pid == 0)//子进程
	{		
		printf("I'm process child, pid = %d\n", getpid());
		sleep(7);			
	} else 
	{

		wpid = wait(NULL);//父进程阻塞等待
		if(wpid == -1)
		{
			perror("wait error");
		}
		printf("I'm parent, I catched child process,pid = %d\n", wpid);
	}

	return 0;
}

在这里插入图片描述
例2:
wait2.c

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

int main(void)
{
	pid_t pid, wpid;
	int status;

	pid = fork();

	if(pid == -1)
	{
		perror("fork error");
		exit(1);
	} 
	else if(pid == 0)
	{	//子进程
		printf("I'm process child, pid = %d\n", getpid());
#if 1
		execl("./abnor", "abnor", NULL);//使用测试用例
		perror("execl error");
		exit(1);
#endif
		sleep(1);				
		exit(10);
	} 
	else 
	{
		
		wpid = wait(&status);	//传出参数

		if(WIFEXITED(status)){	//正常退出
			printf("I'm parent, The child process %d exit normally\n", wpid);
			printf("return value:%d\n", WEXITSTATUS(status));

		} 
		else if (WIFSIGNALED(status))//异常退出 
		{	
			printf("The child process exit abnormally,killed by signal %d\n", WTERMSIG(status));//获取信号编号
										
		} 
		else 
		{
			printf("other...\n");
		}
	}

	return 0;
}

abnor.c

/*测试用例,用以验证wait异常情况*/

int main(void)
{
	char *p = "test of wait abnormally\n";

    //p[0] = 'h';
    int a = 5/0;

	return 56;
}

结果1:对应int a = 5/0;
在这里插入图片描述
结果2:对应p[0] = 'h';
在这里插入图片描述
结果3:对应屏蔽掉测试用例,此时子进程正常结束

#if 0
		execl("./abnor", "abnor", NULL);
		perror("execl error");
		exit(1);
#endif

在这里插入图片描述

waitpid函数

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

pid_t waitpid(pid_t pid, int *status, in options);	

成功:返回清理掉的子进程ID;失败:-1(无子进程)
特殊参数和返回情况:
参数pid:
> 0, 回收指定ID的子进程
-1,回收任意子进程(相当于wait)
0,回收和当前调用waitpid一个组的所有子进程
< -1,如-i表示回收指定进程组i内的任意子进程

返回值为0的情况:参3为WNOHANG,且子进程正在运行时,此时返回值为0。

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

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

int main(void)
{
	pid_t pid,wpid;
	
	pid = fork();
	

	if(pid == -1)
	{
		perror("fork error");
		exit(1);
	} 
	else if(pid == 0)
	{	//子进程
		printf("I'm process child, pid = %d\n", getpid());
		sleep(5);				
		exit(4);
	} 
	else 
	{	//父进程			
		do {
			wpid = waitpid(pid, NULL, WNOHANG);//不阻塞
            
			
			if(wpid == 0)
			{
				printf("子进程%d还未被回收\n",pid);
				sleep(1);		
			}
		} while (wpid == 0);//轮询回收指定子进程

		if(wpid == pid)
		{		//成功回收了指定的子进程
			printf("I'm parent, I catched child process,pid = %d\n", wpid);
		} 
		else 
		{
			printf("other...\n");
		}
	}

	return 0;
}

在这里插入图片描述
例2:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    pid_t pid;
    pid = fork();

    if (pid < 0) {
        perror("fork failed");
        exit(1);
    }

    if (pid == 0) 
	{
        
        printf("This is the child\n");
        sleep(5);
       
        exit(34);
    } 
    else 
    {
        int stat_val;
        waitpid(pid, &stat_val, 0);//指定pid回收子进程,阻塞

        if (WIFEXITED(stat_val))
            printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
        else if (WIFSIGNALED(stat_val))
            printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
    }
    return 0;
}

在这里插入图片描述
例3:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
	int n = 5, i;				
    pid_t p;

	if(argc == 2){	
		n = atoi(argv[1]);
	}
    
	for(i = 0; i < n; i++)//循环创建n个子进程	 
	{
        p = fork();
		if(p == 0) 
		{
			break;//子进程出口			
        } 
    }

	if(n == i)
	{   //父进程
		sleep(n);
		printf("I am parent, pid = %d\n", getpid());
        do{
        	p = waitpid(0, NULL, WNOHANG);//回收任意子进程,非阻塞,因为有多个子进程,所以轮询回收
        	if(p>0)
        	{
        		printf("wait  pid = %d\n", p);
        		n--;
        	}
        	sleep(1);
        
        }while(n>0);
            
            
        
	} 
	else
	{
		sleep(i);
		printf("I'm %dth child, pid = %d\n", i+1, getpid());
	}


	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值