进程基础3 --- 进程源语wait,进程源语waitpid,僵尸进程,校验子进程退出

进程基础3 --- 进程源语wait,进程源语waitpid,僵尸进程,校验子进程退出,第1张

进程基础3 --- 进程源语wait,进程源语waitpid,僵尸进程,校验子进程退出

一、僵尸进程 Zombie Process
1、父子进程模型中,子进程先于父进程结束,父进程必须回收子进程资源(PCB),否则会产生内存泄漏。(僵尸进程)

2、为什么用户空间会被内核回收,而PCB不会,子进程PCB要留给父进程回收,子进程退出的详情保存在PCB中,父进程需要知道子进程的具体因为什么退出(死后验尸QAQ),子进程PCB回收只有父进程能做。(linux强亲缘关系)

3、僵尸进程的危害
1)僵尸进程有比较大的内存泄漏危害。(PCB是庞大的结构体,内部成员众多,许多成员还额外占用内存空间)
2)僵尸进程导致PCB残留,影响新进程的创建。

#include
#include
#include

int main(void)
{
	pid_t pid;
	pid = fork();
	if(pid > 0){
		printf("parent runing...n");
		while(1)
			sleep(1);
	}else if(pid == 0){
		printf("child exit..n");
		sleep(8);
		exit(0); //此代码,子进程退出,会产生僵尸进程
	}else{
		perror("fork call failed");
		exit(0);
	}
	return 0;
}

通过ps aux查看,可观察到状态为Z+的僵尸进程

4、wait函数(父进程通过该函数回收子进程的PCB资源,避免僵进程)

pid_t wpid = wait(int * status);

1)int * status , 当回收成功,会将PCB中子进程的退出状态传出到status变量,父进程可以进行处置与校验,获取子进程结束原因。不想验尸status传空。
2)RETURN VALUE:
1.Wait回收成功,返回僵尸进程pid (wpid > 0)
2.如果返回-1表示回收失败(无子进程调用wait) (wpid == -1)

wait是一个阻塞函数,使用wait回收,父进程则无法执行任何任务,必须等待回收完毕,自由才可以被释放。

#include
#include
#include
#include
int main(void)
{
	pid_t pid;
	pid = fork();
	if(pid > 0){
		pid_t wpid;
		printf("parent %d runing...n",getpid());
		wpid = wait(NULL);
		printf("parent wait %d child n",wpid);
		while(1)
			sleep(1);
	}else if(pid == 0){
		printf("child %d running ...n",getpid());
		sleep(8);
		printf("child %d exit ...n",getpid());
		exit(0); 
	}else{
		perror("fork call failed");
		exit(0);
	}
	return 0;
}



5、waitpid
是wait的进阶版,支持多种参数选项,具备更灵活的回收方式,支持非阻塞轮询回收方案。(单进程模式下父进程可在回收过程中穿插执行自身任务)
默认情况下waitpid也是阻塞回收,可以通过opt设置为非阻塞。

pid_t pid = waitpid(pid_t pid , int * status , int opt);

1)argment pid
-1 : 回收任意子进程
大于0 : 通过pid指定回收一个子进程。
等于0 : 同组回收方案,可以帮助调用进程将本进程组所有子进程回收处理掉。
<-1: 跨组回收方案,-PGID,到指定进程组,回收子进程资源。
2)argment status
回收成功传出子进程的结束原因,父进程可以校验, 如果不需要则传NULL。
3)argment opt
WNOHANG 非阻塞关键字
4)RETURN VALUE
-1 , 表示回收失败(无子进程回收)
大于0 , 表示回收成功(返回僵尸进程的pid > 0)
等于0 , 表示非阻塞返回(TRY AGAIN)
使用waitpid编写非阻塞回收模型,在回收过程中让父进程执行N次自定义任务。

#include
#include
#include
#include

int main(void)
{
	pid_t pid;
	pid = fork();
	if(pid > 0){
		pid_t wpid;
		printf("parent %d runing...n",getpid());
		while((wpid = waitpid(-1, NULL ,WNOHANG)) != -1){
			if(wpid > 0){
				printf("parent wait child success,zombie pid :%dn",wpid);
				break;
			}else{
				printf("parent Try to do a job ... n");
				sleep(1);
			}
		}
		while(1)
			sleep(1);
	}else if(pid == 0){
		printf("child %d running ...n",getpid());
		sleep(8);
		printf("child %d exit ...n",getpid());
		exit(0); 
	}else{
		perror("fork call failed");
		exit(0);
	}
	return 0;
}


但是无论wait还是waitpid 都属于主动回收,无论采取哪种方式,主动回收总会浪费大量资源,前者阻塞,后者不停询问。最理想的是子进程退出了会有消息通知我,然后我去回收,以后会聊到。(通过信号实现)
6、status 子进程的推出原因校验
无论是wait函数还是waitpid函数都可以通过status参数对子进程结束原因进行传出校验。
1) WIFEXITED(status)
校验子进程是否正常退出,如果是返回True。
2)WEXITSTATUS(status)
如果子进程为正常退出,可以通过该函数获取子进程退出码,例如exit(8), 8会被返回。
3)WIFSIGNALED(status)
校验子进程是否被信号杀死,如果是返回True。
4)WTERMSIG(status)
子进程被信号杀死,可以利用该函数返回终止子进程的信号编号。

#include
#include
#include
#include


int main(void)
{int status;
	pid_t pid;
	pid = fork();
	if(pid > 0){
		pid_t wpid;
		printf("parent %d runing...n",getpid());
		while((wpid = waitpid(-1, &status ,WNOHANG)) != -1){
			if(wpid > 0){
				printf("parent wait child success,zombie pid :%dn",wpid);
				if(WIFEXITED(status))
					printf("child %d process exit code %dn",wpid,WEXITSTATUS(status) );
				if(WIFSIGNALED(status))
					printf("kill %d child process signal %dn",wpid,WTERMSIG(status) );
				break;
			}else{
				printf("parent Try to do a job ... n");
				sleep(1);
			}
		}
		while(1)
			sleep(1);
	}else if(pid == 0){
		printf("child %d running ...n",getpid());
		sleep(8);
		printf("child %d exit ...n",getpid());
		while(1);//signal 杀死 先卡住等待外部信号 方便测试
		//exit(8);  //正常推迟
	}else{
		perror("fork call failed");
		exit(0);
	}
	return 0;
}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5704233.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存