Linux中的僵尸进程与wait函数、waitpid函数详解(附图解与代码实现)

目录

僵尸进程的概念及危害

wait函数

waitpid函数

通过status进行僵尸进程校验


僵尸进程的概念及危害

僵尸进程:子进程先于父进程退出,但是系统回收进程资源时,子进程PCB残留,这个进程为僵尸进程

危害:1.PCB残留导致内存泄露

           2.由于PCB创建的数量是固定的,一个僵尸进程会占用一个PCB,从而影响新进程的创建

为什么PCB会有残留呢?

我们假设张三的孩子张思睿出了车祸,医院没救过来,结果医院直接一把火给尸体烧了,你说这合理吗?这显然不合理只有父母有权来决定自己的孩子尸体怎么处理,父母可以通过别的方式来知道自己的孩子是怎么死的,同理,父进程可以通过查看子进程残留的PCB来知道子进程是怎么结束的,这也就是子进程残留PCB的原因

怎么解决僵尸进程这一问题呢?

这里就要用到wait函数和waitpid函数,僵尸进程产生后,只有父进程可以进行回收处理

父进程回收子进程的PCB,可以处理内存泄露,还可以对子进程的退出原因进行校验,通过PCB校验,掌握子进程的退出原因

wait函数

功能:父进程可以调用该函数来清除僵尸进程

pid_t zpid = wait(int* status);//僵尸的英文是zombie,所以这个地方变量名设置为zpid

这是经典的阻塞回收函数,如果有子进程存在,那么就一直阻塞等待子进程死亡,然后回收

头文件  #include<sys/wait.h>

status(输出参数)就是状态,父进程可以通过status来知晓子进程退出的原因,并回收释放PCB

如果传入的是NULL,就只回收释放PCB,不传出子进程退出原因

返回值:

成功就返回僵尸进程的pid(>0),失败就返回-1(没有子进程)

以下是wait函数清除僵尸进程的代码实现

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>//wait函数的头文件

int main(void)
{
	pid_t pid;
	pid = fork();
	if (pid > 0)
	{
		pid_t zpid;
		printf("parent %d running\n...\n", getpid());
		if ((zpid = wait(NULL)) > 0)//成功的话wait函数的返回值大于0
        //回收成功,返回僵尸进程的zpid,NULL-->不返回子进程退出原因
		{
			printf("parent wait success zombie pid %d\n", getpid());
		}
		while (1)
		{
			sleep(1);
		}
	}
	else if (pid == 0)
	{
		printf("Child %d Running\n...\n", getpid());
		sleep(10);
		exit(0);
		//这个地方,子进程退出,PCB残留,产生僵尸进程,进入判断if ((zpid = wait(NULL)) > 0)
	}
	else
	{
		printf("fork call failed\n");
		exit(0);
	}
	return 0;
}

waitpid函数

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

支持非阻塞回收,如果当前子进程无法回收则返回

头文件:#include<sys/wait.h>

Pram:

pid回收方式
-1回收任意子进程
>0

指定一个子进程的pid,然后回收该子进程

0

 可以回收与调用进程(父进程)同组的所有子进程(同组回收)

< -1

 可以回收与调用进程(父进程)同组的所有子进程(同组回收)

Status    校验子进程的退出原因,传入NULL则不校验

Opt  设置waitpid的工作模式,传入参数WNOHANG,则将waitpid改为非阻塞工作模式

返回值:

回收成功返回僵尸进程的zpid

回收失败返回 -1(表示没有子进程)

非阻塞返回0(表示当前子进程不可回收,立即返回)

以下是waitpid函数清除僵尸进程的代码实现

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

int main(void)
{
	pid_t pid;
	pid = fork();
	if (pid > 0)
	{
		pid_t zpid;
		while ((zpid = waitpid(-1, NULL, WNOHANG)) != -1)
	    //因为我们传入了WNOHANG,waitpid函数现在为非阻塞模式
		//那么就有两种情况,返回值>0 和 ==0 两种情况
		{
			if (zpid > 0)
			{
				printf("parent wait success , zpid = %d\n", zpid);
			}
			else if (zpid == 0)
			{
				printf("Parent Running...\n");
				sleep(1);
			}
		}
	}
	else if (pid == 0)//到了子进程,子进程进入该判断
	{
		sleep(10);
		exit(0);
	}
	return 0;
}

wait函数和waitpid函数的区别

相比于阻塞回收,非阻塞回收更灵活,可以释放父进程让其执行其他的任务

通过status进行僵尸进程校验

前面只是说了父进程怎么回收僵尸进程,而没有考虑查询子进程退出变成僵尸进程的原因

那么接下来我们说一下通过status进行僵尸进程的校验的过程,先看下下面这张图

 校验过程:
1.父进程调用wait/waitpid函数,其中的参数status得到了子进程的退出原因

2.进入两个判断,分别是子进程正常退出和子进程异常退出的两个判断

注意,这两个判断不是if--else的关系,而是要用两个if判断

3.如果子进程为正常退出,比如像return 0;   exit(0);    exit(8)这样退出即为正常退出,那么WIFEXITED(status)的返回值为true,可以通过调用WEXITSTATUS(status)来获取子进程的退出码

4.如果子进程为异常退出,比如像因为硬件访问错误这样退出即为异常退出,那么WIFSIGNALED(status)的返回值为true,可以通过调用WTERMSIG(status)获取杀死子进程的信号编号

以下是通过status进行僵尸进程校验的过程的代码实现

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

int main(void)
{
	pid_t pid;
	int i;
	for (i = 0; i < 2; i++)//循环创建了两个子进程
	{
		pid = fork();
		if (pid == 0)
		{
			break;
		}
	}
	if (pid > 0)
	{
		int status;
		printf("Parent pid %d\n Wait..\n", getpid());
		pid_t zpid;
		while ((zpid = wait(&status)) > 0)//子进程退出校验
		{
			if (WIFEXITED(status))
			{
				printf("Parent Wait Success , Zombie pid %d exit error Print ExitCode %d\n", zpid, WEXITSTATUS(status));
			}
			if (WIFSIGNALED(status))
			{
				printf("Parent Wait Success , Zombie pid %d exit error Signal No %d\n", zpid, WTERMSIG(status));
			}
		}
	}
	else if (pid == 0)
	{
		if (i == 0)
		{
			printf("First child pid %d Exit...\n", getpid());
			exit(7);
		}
		else if (i == 1)
		{
			while (1)
			{
				printf("Child pid %d while(1) sleep..\n", getpid());
				sleep(1);
			}
		}
	}
	else
	{
		perror("fork call failure\n");
		exit(0);
	}
	return 0;
}

今天的学习记录到此结束啦,咱们下篇文章见,ByeBye!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_才疏学浅_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值