18进程学习之waitpid回收循环多个子进程

1 概述
我们知道一次wait或waitpid调用只能清理一个子进程,所以清理多个子进程应使用循环。并且即使你参1传的是-1,它的含义代表回收所有父进程的子进程,但是每次都是一个个回收即多次回收多个子进程,并非一次回收所有子进程。

2 代码例子
1)循环回收多个子进程。

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

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

	for(i = 0; i < 5; i++)	 {
		pid = fork();
		if(pid == 0) {
			break;
        } 
    }

	if(5 == i){
		while(wpid = waitpid(-1,NULL,0)){//阻塞形式循环回收
			if(wpid > 0){
				printf("waitpid child pid = %d\n", wpid);//打印回收的子进程
			}
			else if(wpid == 0){//实际上阻塞回收的话这一步不会执行,因为子进程肯定执行完了父进程才会进,并且进>0那里
				sleep(1);
				printf("111\n");
				continue;
			}
			else{
				printf("Not can waitpid child,return value = %d.\n",wpid);//返回-1说明回收出错即没有可回收的子进程
				break;
			}
		}
	} 
	else {
		sleep(i);//子进程按序输出
		printf("I'm %dth child, pid = %d, gpid=%d\n", i+1, getpid(), getgid());
	}

	return 0;
}

结果,可以看到,每个子进程间隔的被父进程回收,然后没有子进程可回收时返回-1退出while。
造成间隔回收子进程的原因分析:
在父进程中,我们循环fork了五个子进程,理应来说父进程由于循环for可能慢点执行,但是由于子进程存在sleep(i)秒,所以与睡眠相比,父进程for循环消耗时间是非常少的,所以父进程一早就在waitpid等待子进程睡眠结束回收了,所以就造成每当一有子进程结束,父进程就可回收的间隔现象了。
实际上你可以把子进程的睡眠注释掉,可以看到子进程结束非常快,父进程速度慢而回收不够快只能最后慢慢回收了。看图二。
在这里插入图片描述

图二如下:
在这里插入图片描述

2)非阻塞回收多个子进程。

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

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

	for(i = 0; i < 5; i++)	 {
		pid = fork();
		if(pid == 0) {
			break;
        } 
    }

	if(5 == i){
		/*
		while(wpid = waitpid(-1,NULL,0)){//阻塞形式循环回收
			if(wpid > 0){
				printf("waitpid child pid = %d\n", wpid);//打印回收的子进程
			}
			else if(wpid == 0){//实际上阻塞回收的话这一步不会执行,因为子进程肯定执行完了父进程才会进,并且进>0那里
				sleep(1);
				printf("111\n");
				continue;
			}
			else{
				printf("Not can waitpid child,return value = %d.\n",wpid);//返回-1说明回收出错即没有可回收的子进程
				break;
			}
		}
		*/
		while((wpid = waitpid(-1,NULL,WNOHANG)) != -1){//非阻塞形式循环回收,必须在while判断且必须用括号括起运算表达式
			if(wpid > 0){
				printf("waitpid child pid = %d\n", wpid);//打印回收的子进程
			}
			else if(wpid == 0){//非阻塞回收,父进程速度快,子进程极大可能未结束,所以返回0,让父进程继续回收即可
				sleep(1);
				printf("Continue waitpid.\n");
				continue;
			}
			//else{
				//printf("Not can waitpid child,return value = %d.\n",wpid);//返回-1说明回收出错即没有可回收的子进程,但若还有子进程存活的话这里不会执行,因为上面continue了
				//break;
			//}
		}
	} 
	else {
		sleep(i);//子进程按序输出
		printf("I'm %dth child, pid = %d, gpid=%d\n", i+1, getpid(), getgid());
	}

	return 0;
}

结果可以看到,可以正确回收五个子进程。
在这里插入图片描述

非阻塞回收的错误写法1):
返回值没有在while判断:

		while((wpid = waitpid(-1,NULL,WNOHANG))){//非阻塞形式循环回收,不在while判断导致父进程没有可回收的子进程时返回0,while直接退出
			printf("wpid = %d\n",wpid);//可以看到这行并没有并打印
			if(wpid > 0){
				printf("waitpid child pid = %d\n", wpid);//打印回收的子进程
			}
			else if(wpid == 0){//非阻塞回收,父进程速度快,子进程极大可能未结束,所以返回0,让父进程继续回收即可
				sleep(1);
				printf("Continue waitpid.\n");
				continue;
			}
			else{
				printf("Not can waitpid child,return value = %d.\n",wpid);//返回-1说明回收出错即没有可回收的子进程,但若还有子进程存活的话这里不会执行,因为上面continue了
				break;
			}
		}

判断必须在while中判断,因为非阻塞时,父进程执行非常快而子进程没有结束,导致wpid返回的是0而直接退出了while循环,在while打印数据可以看到并没有输出任何内容,所以父进程直接返回了并且在终端中第一时间打印出来。
看第二行,由于父进程直接退出while导致终端描述符第一个被打印出来。进而导致最后一行出现伪死状态即让人以外卡主。
所以循环回收多个子进程时判断必须在while括号中执行。
在这里插入图片描述

非阻塞回收的错误写法2):
在while循环中判断,没有用括号将运算表达式用括起:

		while(wpid = waitpid(-1,NULL,WNOHANG) != -1){//非阻塞形式循环回收,必须用括号括起运算表达式
			if(wpid > 0){
				printf("waitpid child pid = %d\n", wpid);//打印回收的子进程
			}
			else if(wpid == 0){//非阻塞回收,父进程速度快,子进程极大可能未结束,所以返回0,让父进程继续回收即可
				sleep(1);
				printf("Continue waitpid.\n");
				continue;
			}
			//else{
				//printf("Not can waitpid child,return value = %d.\n",wpid);//返回-1说明回收出错即没有可回收的子进程,但若还有子进程存活的话这里不会执行,因为上面continue了
				//break;
			//}
		}

在while循环中判断,必须给整个运算表达式用括号括起来,否则gcc认为它一直为真值(可加上-Wall获取警告)即wpid=1,导致无限循环打印wpid>0的内容,即下图wpid = 1那行内容。
在这里插入图片描述

3 总结
循环回收多个子进程时,必须在while中判断且要用括号括起整个运算表达式。

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值