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

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

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

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

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>

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 :%d\n",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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>


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 :%d\n",wpid);
				if(WIFEXITED(status))
					printf("child %d process exit code %d\n",wpid,WEXITSTATUS(status) );
				if(WIFSIGNALED(status))
					printf("kill %d child process signal %d\n",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;
}

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值