万泰注册
业务范围你的位置:万泰注册 > 业务范围 >

Linux系统编程之waitpid函数详解_linux waitpid

发布日期:2024-09-02 11:07    点击次数:59

  

5.1 为什么要进行进程资源的回收

        当一个子进程退出之后,该进程能够回收自己的用户区的资源,但是不能回收内核空间区的PCB(process control block 进程控制块)资源。

(即:子进程自己本身是无法完成对内核的PCB资源的回收,此时就会非常浪费linux系统的资源了!)

        这个子进程内核区的PCB资源必须得由它的父进程,调用wait 或者 waitpid函数完成对其子进程的回收,从而避免了对系统资源的浪费!

图片

5.2 孤儿进程

(本身不是错误!因为孤儿进程还是可以存活着的,可以给init进程回收其内核区的PCB资源的,一切都还是正常的/可接受的case)

        孤儿进程的概念:

        若子进程的父进程已经死掉(先于子进程执行退出),而子进程还活着,那么这个子进程此时就会变成孤儿进程了!

        (在Linux操作系统下,是必须要保证每一个进程都有一个父进程的)为了保证每个进程都有一个父进程,孤儿进程会被一个进程id PID==1的叫做init的进程回收掉(领养),那么init进程就成为了该孤儿进程的养父进程了。当孤儿进程执行退出之后,由init进程完成对孤儿进程资源的回收。

图片

        模拟孤儿进程的案例: 

        编写模拟孤儿进程的代码,并验证孤儿进程的父进程是否由原来的父进程变为init进程。

test codes1:

//测试代码,测试父进程先死掉(先执行退出)后,是否由于PID==1的init进程把该子进程领养了!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	//创建子进程函数原型:	//pid_t fork(void);	pid_t pid = fork();	if(pid < 0)//fork失败的case	{		perror("fork error!");		return -1;	}	else if(pid == 0)//pid == 0 时,则当前进程为子进程	{		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());	}	else//pid > 0 时,则当前进程为父进程	{ 		//pid_t getpid(void);		//这个函数会返回调用该函数的进程的ID		//(哪个进程调用它,它就返回谁的PID)		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());		sleep(2);//让父进程后退出,此时就不会出现孤儿子进程的case了!	}	return 0;}

result1:

 

图片

 

此时属于正常case,不会出现僵尸子进程的case! (因为其父进程sleep了2s,等子进程先结束)

补充:

        pid_t getpid(void);

        这个函数会返回调用该函数的进程的ID

        (哪个进程调用它,它就返回谁的PID)

        pid_t getppid(void);

        这个函数会返回调用该函数的进程的父进程的ID

        (哪个子进程调用它,它就返回它的父进程的PID)

 test codes2:

//测试代码,测试父进程先死掉(先执行退出)后,是否由于PID==1的init进程把该子进程领养了!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<string.h>#include<stdlib.h>int main(){	//创建子进程函数原型:	//pid_t fork(void);	pid_t pid = fork();	if(pid < 0)//fork失败的case	{		perror("fork error!");		return -1;	}	else if(pid == 0)//pid == 0 时,则当前进程为子进程	{		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());	}	else//pid > 0 时,则当前进程为父进程	{ 		//pid_t getpid(void);		//这个函数会返回调用该函数的进程的ID		//(哪个进程调用它,它就返回谁的PID)		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());	}	return 0;}

result2:

图片

图片

小总结:

        孤儿进程:当父进程先退出,子进程后退出时,子进程就变成孤儿进程了,此时由init进程(PID==1)回收。当子进程也退出之后(他自己会回收自己用户区的资源),而init进程则会回收养子进程的内核区PCB资源。

5.3 僵尸进程(在开发项目时,一定要注意僵尸进程的问题!不要给我写出)

(本身就是个错误!因为僵尸进程是已经死掉了的,如果不杀死其原父进程---给init进程回收其内核区的PCB资源的话,此时系统就一直无法回收其内核区PCB资源,这样迟早你的系统资源给消耗掉,消耗完了你的系统就无法再创建新的进程了。一旦系统无法创建新的进程,这将会是很可怕的事情!此时就是不正常的/不可接受的case)

        僵尸进程的概念:

        若子进程死了,父进程还活着,但是父进程没有调用wait 或者 waitpid函数完成对子进程的回收时(或者说父进程调用waitpid函数对其子进程进行回收时,子进程还活着,且waitpid函数的options==WNOHANG非阻塞的),此时的子进程就变成僵尸进程。

(当当子进程先退出,而父进程没有完成对子进程资源的回收,此时的子进程就会变成僵尸进程!)

        补充:在ps ajx命令下,你可以看到所有关于进程的信息,此时,假设某父进程名字为zombie,若你还看到有[zombie]<defunct>这样的进程,就说明此时你的系统存在着僵尸进程!<defunct>这个关键字就标识了这个进程为僵尸进程!因为defunct是adj,消失的,不存在的意思!

        如何解决 僵尸进程(不回收导致的系统资源浪费)的问题呢?

        答:

        临时的非最佳的解决方法是:要想把僵尸进程的资源马上给回收掉,需要你通过使用kill -9 pid命令,直接杀掉其对应的父进程(因为僵尸子进程已经是个死掉了的进程,它没法接受并回应你给它传的kill -9 僵尸子进程pid这样的杀死自己的命令)。

        原因:杀死僵尸子进程的原父进程,可以让该僵尸子进程给init进程领养,由init进程来do回收它的资源的工作。这就可以避免僵尸子进程的资源没有被回收而导致的系统资源的浪费!

        最佳的最好的解决方法是:在子进程先于父进程退出后,让父进程调用wait或者waitpid函数对其资源进行回收!(这个后面学到wait以及waitpid函数时就会讲到!下面先对临时的解决方法写一个代码do验证测试!)

        模拟僵尸进程的案例:

        编写模拟僵尸进程的代码,验证若子进程先于父进程退出,而父进程又没有调用wait或者waitpid函数对其进程内核区PCB资源的回收,从而是的子进程变成了僵尸进程。

test codes:

//测试代码,测试子进程先死掉(先执行退出)后,父进程没有回收子进程的资源//此时的子进程就会变成僵尸进程了!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	//创建子进程函数原型:	//pid_t fork(void);	pid_t pid = fork();//fork开一个子进程!	if(pid < 0)//fork失败的case	{		perror("fork error!");		return -1;	}	else if(pid == 0)//pid == 0 时,则当前进程为子进程	{		printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());	}	else//pid > 0 时,则当前进程为父进程	{ 		sleep(200);//父进程休眠100s		//让父进程后退出,此时就不会出现孤儿子进程的case!		//但是,此时就会出现子进程资源没有被父进程回收的case!		//因此,此时的子进程就会变成僵尸进程!		printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());	}	return 0;}

result:

图片

输入命令ps ajx后:

图片

注:这个[zombie]<defunct>就代表了僵尸子进程!因为子进程的用户区(包含名称等的信息)是由父进程copy过去的,所有子进程的名字和父进程的名字是一样的!(这个小点是你必须要care的 !必须要注意的!)

输入命令kill -9 1835252(尝试kill掉僵尸子进程)且 输入命令ps ajx后:

图片

图片

这说明,僵尸进程已经死掉了,你没法用活的kill -9命令去删除它!

那么我们如何让该僵尸进程给马上回收呢?

答:直接杀死该僵尸子进程的父进程,然后该僵尸进程马上就给init进程领养了,领养之后由init进程把这个僵尸进程的资源回收掉!

kill 僵尸进程的父进程 且 输入命令ps ajx后:

图片

图片

        解决上述僵尸进程问题的最佳方案:在子进程先于父进程退出后,马上让父进程调用wait或者waitpid函数对其进行资源的回收工作!

        因此,下面我们将学习wait以及waitpid这两个进程回收函数!

5.4 进程回收函数

不论是wait/waitpid函数,都是在父进程中调用该函数以完成对其子进程资源的回收工作的!

       ①wait函数(是在父进程中调用该函数以完成对其子进程资源的回收工作)

                函数原型:

                pid_t wait(int* wstatus);

                //若不关心进程的结束状态(结束原因)时,可以传NULL进去!

                函数作用:

                        1-- 阻塞并等待子进程退出

                        (注意:这是个阻塞函数!一直阻塞,等到子进程退出后该函数才能返回!)

                        2-- (由父进程)回收子进程残留资源

                        3-- 获取子进程结束状态(退出原因)

                函数返回值:

                        if成功:清理掉子进程ID;

                        if失败:返回-1(表示子进程全都回收掉了,没得子进程回收了)

                wstatus 参数(可以通过man 2 wait命令查询):子进程的退出状态 -- 传出参数

(只要你不传NULL的wstatus进去,后续需要使用wstatus参数来写代码了解子进程退出的状态的,否则,你根本就无需使用wstatus参数的写代码了)

        (这些宏参数,不需要你记住!你只需要会查阅,找到使用方法即可!)

                        WIFEXITED(wstatus):为非0 -> 进程正常结束

图片

                        WEXITSTATUS(wstatus):获取进程退出状态

图片

                        WIFSIGNALED(wstatus):为非0 -> 进程异常终止(此时进程时被信号signal杀死的,比如kill -9 pid信号)

图片

                        WTERMSIG(wstatus):取得 使进程终止的信号的编号

图片

wait 函数练习案例:

test codes1:

//wait函数测试代码,测试让父进程调用wait函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>//这个头文件可以 在 man 2 wait中看到!#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	//循环创建3个子进程	for(i = 0;i<3;++i){		pid_t pid = fork();//创建子进程		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid == 0){// 子进程			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(2);//让子进程休眠2s,以确认其父进程中的wait函数确实是阻塞函数!			break;//这个break就保证了所创建的进程均为兄弟进程!		}		else //pid > 0 父进程		{			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			pid_t exit_pid = wait(NULL);//wait函数会阻塞等待子进程退出后,将其回收!			if(exit_pid == -1){				printf("There is no child process already!");				break;			}			printf("wait函数成功返回!\nexit_pid==child_pid==[%d]\n",exit_pid);		}		printf("\n");	}	return 0;}

result1:

图片

 test codes2:

//wait函数测试代码,测试让父进程调用wait函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	//循环创建3个子进程	for(i = 0;i<2;++i){		pid_t pid = fork();//创建子进程		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid == 0){// 子进程			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(2);			break;//这个break就保证了所创建的进程均为兄弟进程!		}		else //pid > 0 父进程		{			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			int wstatus = -1;			pid_t exit_wait_pid = wait(&wstatus);//wait函数会阻塞等待子进程退出后,将其回收!			if(exit_wait_pid == -1){				printf("There is no child process already!");				break;			}			if(WIFEXITED(wstatus)){//if WIFEXITED(wstatus) == true 子进程正常退出				printf("子进程 正常退出!\n");				//WEXITSTATUS(wstatus));返回子进程退出的状态(原因)				printf("子进程退出的状态(原因):%d\n",WEXITSTATUS(wstatus));			}			else if(WIFSIGNALED(wstatus)){//if WIFSIGNALED(wstatus) == true 子进程是被信号杀死而退出的!				int sigNumber = WTERMSIG(wstatus);//WTERMSIG(wstatus);返回杀死子进程的信号number				printf("子进程 由信号 signal[%d] 杀死了!\n",sigNumber);			}			printf("wait函数成功返回!\nexit_wait_pid==child_pid==[%d]\n",exit_wait_pid);		}		printf("\n");	}	return 0;}

 result2:

图片

test codes3:

//wait函数测试代码,测试让父进程调用wait函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	//循环创建3个子进程	for(i = 0;i<2;++i){		pid_t pid = fork();//创建子进程		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid == 0){// 子进程			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(200);			break;//这个break就保证了所创建的进程均为兄弟进程!		}		else //pid > 0 父进程		{			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			int wstatus = -1;			pid_t exit_wait_pid = wait(&wstatus);//wait函数会阻塞等待子进程退出后,将其回收!			if(exit_wait_pid == -1){				printf("There is no child process already!");				break;			}			if(WIFEXITED(wstatus)){//if WIFEXITED(wstatus) == true 子进程正常退出				printf("子进程 正常退出!\n");				//WEXITSTATUS(wstatus));返回子进程退出的状态(原因)				printf("子进程退出的状态(原因):%d\n",WEXITSTATUS(wstatus));			}			else if(WIFSIGNALED(wstatus)){//if WIFSIGNALED(wstatus) == true 子进程是被信号杀死而退出的!				int sigNumber = WTERMSIG(wstatus);//WTERMSIG(wstatus);返回杀死子进程的信号number				printf("子进程 由信号 signal[%d] 杀死了!\n",sigNumber);			}			printf("wait函数成功返回!\nexit_wait_pid==child_pid==[%d]\n",exit_wait_pid);		}		printf("\n");	}	return 0;}

result3:(演示用kill -9 pid 命令杀死子进程的wait函数的返回状态的效果)

图片

 

图片

 wait函数小总结:

        pid_t wait(int* wstatus);

        注:若对子进程的退出状态不感兴趣的话,可以直接传NULL进去即可!

        参数:

        wstatus:子进程的退出状态

if(WIFEXITED(wstatus)){    WEXITSTATUS(wstatus)}else if(WIFSIGNALED(wstatus)){    WTERMSIG(wstatus)}

        ②waitpid函数(比wait函数更高级,功能也更加强大,也是在父进程中调用该函数以完成对其子进程资源的回收工作)

                函数原型:

                pid_t waitpid(pid_t pid,int* wstatus,int options);

                注:这个waitpid函数默认case下就是阻塞的函数!

                函数作用:(同wait函数)

                        1-- 阻塞并等待子进程退出

                        (注意:这也是个阻塞函数!一直阻塞,等到子进程退出后该函数才能返回!)

                        2-- (由父进程)回收子进程残留资源

                        3-- 获取子进程结束状态(退出原因)

                函数参数:

                pid参数:

                        1-- 当pid == -1时 等待任一子进程。与wait等效(多用)    

                        (人话:就是回收所有(多个)的子进程)

(当然,父进程每调用一次的waitpid函数就只能够回收一个子进程而已,一次该函数的调用并不能回收多个子进程!)

                        2-- 当pid > 0时 等待其进程ID 与 pid 相等的子进程(多用)    

                        (人话:就是回收指定(一个)PID的子进程)

                        3-- 当pid == 0时 等待进程组ID 与 目前进程相同的任何子进程。(少用/基本不用)     

                        (人话:回收任何和调用waitpid()函数的父进程在同一个进程组里面的所有程。)

                        (进程都是有所属组的)

                        4-- 当pid < -1时 等待其组ID 等于 pid的绝对值得任一子进程。(少用/基本不用)   

                        (人话:回收在其他组的子进程)

                wstatus 参数(可以通过man 2 wait命令查询):子进程的退出状态 -- 传出参数 用法同wait函数。

(只要你不传NULL的wstatus进去,后续需要使用wstatus参数来写代码了解子进程退出的状态的,否则,你根本就无需使用wstatus参数的写代码了)

        (这些宏参数,不需要你记住!你只需要会查阅,找到使用方法即可!)

                       WIFEXITED(wstatus):为非0 -> 进程正常结束

图片

                        WEXITSTATUS(wstatus):获取进程退出状态

图片

                        WIFSIGNALED(wstatus):为非0 -> 进程异常终止(此时进程时被信号signal杀死的,比如kill -9 pid信号)

图片

                        WTERMSIG(wstatus):取得 使进程终止的信号的编号

图片

                options参数:

                        1-- 设置为options  == 0,表示waitpid函数为阻塞的。(该函数会阻塞卡在这儿,若有子进程,就回收;若没有子进程,一直卡着)

                        2-- 设置为options == WNOHANG,表示waitpid函数为非阻塞的(也即不管有没有子进程了,该函数都不会阻塞卡在这儿)

                        (通常,当你想把waitpid函数设置为非阻塞的时候(即让该函数不影响别的程序的执行,别只是卡在这儿),就让options == WNOHANG)

图片

                函数返回值:

                 >0:表示返回的是回收掉的子进程ID(PID);

                  -1:表示无子进程了;(表示子进程全都回收掉了,没得子进程回收了)

                 =0 且 第3个参数options为WNOHANG时:表示子进程正在运行(子进程尚未退出);

在linux的终端下输入man 2 waitpid/wait命令可查询到:

图片

                waitpid函数案例练习:

test codes1:

//waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	for(;i<3;++i){		//循环创建3个子进程		pid_t pid = fork();		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid==0){//当前进程为 子进程			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(2);			break;//这个break语句会保证所创建的子进程都为兄弟子进程		}		else{//pid > 0 当前进程为 父进程			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			//在父进程中调用waitpid函数!			pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程            //即等待着任一子进程do回收的意思			int* wstatus = NULL;//status == NULL表示我不关心子进程的退出状态			int options = 0;//options==0表示该函数是阻塞的			pid_t exit_waitpid_pid = waitpid(inputPid,wstatus,options);			if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!				printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);			}			else if(exit_waitpid_pid == -1){				printf("当前没有需要回收掉的子进程了!\n");			}			else if(exit_waitpid_pid == 0 && options == WNOHANG){				printf("当前需要回收的子进程还在运行中,尚未退出!\n");			}		}		printf("\n");	}	return 0;}

result1:

图片

test codes2:

//waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	for(;i<3;++i){		//循环创建3个子进程		pid_t pid = fork();		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid==0){//当前进程为 子进程			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(2);			break;//这个break语句会保证所创建的子进程都为兄弟子进程		}		else{//pid > 0 当前进程为 父进程			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			//在父进程中调用waitpid函数!			pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程			int wstatus = -1;//&status != NULL表示我关心子进程的退出状态								//后续需要结合wstatus的参数写代码了解子进程的退出状态			int options = WNOHANG;//options==WNOHANG表示该函数是不阻塞的			pid_t exit_waitpid_pid = waitpid(pid,&wstatus,options);			if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!				printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);				//成功回收掉子进程,才能够判断其退出状态!				if(WIFEXITED(wstatus)){					printf("子进程正常退出了!子进程的退出状态是:%d\n",WEXITSTATUS(wstatus));				}else if(WIFSIGNALED(wstatus)){					printf("子进程时由信号[%d]杀死的!\n",WTERMSIG(wstatus));				}			}			else if(exit_waitpid_pid == -1){				printf("当前没有需要回收掉子进程了,即:活着的子进程都已经死掉了退出了!\n");			}			else if(exit_waitpid_pid == 0 && options == WNOHANG){				printf("当前需要回收的子进程还在运行中,尚未退出!\n");			}			sleep(100);//让子进程变成僵尸进程!		}		printf("\n");	}	return 0;}

        解释:因为此刻,父进程调用waitpid函数对其子进程进行回收时,子进程还活着,且waitpid函数的options==WNOHANG非阻塞的),这样父进程又休息了100s,子进程才休息2s,因此子进程会先退出,此时父进程又没回收它的资源。因此,此时的子进程就变成僵尸进程了。 

result2:(这个test2代码会产生3个僵尸进程,这是所用waitpid的一个弊端,你可以用临时回收子进程的方法:直接杀掉其对应的原父进程!让init进程领养它,并对其资源进行回收,这样才能避免浪费系统资源!)

图片

图片

 

图片

图片

图片

图片

但是,用临时回收子进程的方法还是不太好的,因此我们对test2这种,父进程调用非阻塞的waitpid函数对其子进程回收时所遇到的问题的代码,进行改进,得到一份robust的代码!

技巧:一直循环回收子进程的代码!知道没有子进程了/子进程都给回收掉了!

so,下面这份代码是父进程调用非阻塞式的waitpid函数对其子进程do回收工作的标准代码范本:

test codes3:

//waitpid函数测试代码,测试让父进程调用waitpid函数回收子进程资源!#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>#include<string.h>#include<stdlib.h>int main(){	printf("before fork: pid==[%d]\n",getpid());	int i = 0;	for(;i<3;++i){		//循环创建3个子进程		pid_t pid = fork();		if(pid < 0){			perror("fork error!");			return -1;		}		else if(pid==0){//当前进程为 子进程			printf("\n");			printf("第[%d]个子进程被父进程do循环回收工作:",i+1);			printf("child process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			sleep(1);			break;//这个break语句会保证所创建的子进程都为兄弟子进程		}		else{//pid > 0 当前进程为 父进程			printf("father process: pid==[%d],fpid==[%d]\n",getpid(),getppid());			//在父进程中调用waitpid函数!			while(1){				//这个while循环,就已经可以保证 父进程一定是晚于子进程退出的了!				pid_t inputPid = -1;//inputPid==-1表示我要回收当前所有的子进程				int wstatus = -1;//&status != NULL表示我关心子进程的退出状态									//后续需要结合wstatus的参数写代码了解子进程的退出状态				int options = WNOHANG;//options==WNOHANG表示该函数是不阻塞的				pid_t exit_waitpid_pid = waitpid(pid,&wstatus,options);				if(exit_waitpid_pid > 0){//表示成功回收掉子进程了!					printf("成功回收掉子进程了,此时所回收掉的子进程id==[%d]\n",exit_waitpid_pid);					//成功回收掉子进程,才能够判断其退出状态!					if(WIFEXITED(wstatus)){						printf("子进程正常退出了!子进程的退出状态是:%d\n",WEXITSTATUS(wstatus));					}else if(WIFSIGNALED(wstatus)){						printf("子进程时由信号[%d]杀死的!\n",WTERMSIG(wstatus));					}				}				else if(exit_waitpid_pid == 0 && options == WNOHANG){					// printf("当前需要回收的子进程还在运行中,尚未退出!\n");					//因为这句子进程尚在运行的话肯定是大量打印的!                    //so这些测试代码可以在测试程序时使用,但是在实际项目中就注释掉了吧!				}				else if(exit_waitpid_pid == -1){					printf("当前没有需要回收掉子进程了,即:活着的子进程都已经死掉了退出了!\n");					printf("此时,没有需要回收掉子进程了,就直接break 退出循环,无需再do回收子进程资源的工作了!\n");					break;//此时,没有需要回收掉子进程了,就直接break 退出循环,无需再do回收子进程资源的工作了!				}			}		}		printf("\n");	}	return 0;}

result3:

图片

waitpid函数小总结:

waitpid函数:pid_t waitpid(pid_t pid,int* status,int options);    参数:        pid:            pid > 0:表示等待指定的子进程            pid==-1:表示等待任一子进程        status:        同wait函数        options:            0:表示阻塞            WNOHANG:表示不阻塞(不论有没有子进程退出,waitpid函数都会马上退出!不会阻塞在这儿!)    返回值:        > 0:是回收掉子进程的PID        = 0:若options取值为WNOHANG,则表示子进程还活着        -1:表示目前已经没有子进程了,此时那可以break掉循环回收子进程的while循环了!    注意:调用一次waitpid/wait函数只能回收一个子进程而已!灵魂拷问:为什么一定是由于父进程来调用wait/waitpid函数来对其子进程的资源进行回收呢?答:因为子进程无法对自己的内核区PCB的资源进行回收!这个进程控制块的资源必须由其父进程去回收!一旦子进程先于父进程先退出,则此时的子进程就会变成僵尸进程!那么就会浪费系统资源!

小练习:

图片

图片

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报。

Powered by 万泰注册 @2013-2022 RSS地图 HTML地图