在我们介绍本篇文章之前,我想介绍一下退出码,还记得C语言中main函数中都有return 0嘛,这个return 0就是退出码,我们执行程序无非就三种情况:1.执行结束,和预期一致 2.执行结束,和预期不一致 3.执行结束前,程序出错 如果子进程终止,但是父进程没有做任何操作会导致子进程一直处于这种状态。 例如我们在main函数里面写return 10,使用**echo ?
那么这个退出码怎么获取呢?我们可以写如下的程序获取退出码的含义
#include<stdio.h>
#include<string.h>
int main()
{
int i=0;
for(i=0;i<200;++i)
{
printf("%d %s\n",i,strerror(i));
}
}
_exit和exit的区别
_exit是系统调用不会刷新缓冲区,exit是库函数
僵尸进程的危害
1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的! 2.维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的 3.那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间
我们的解决方法是:父进程通过进程等待的方式,回收子进程资源
一、进程等待的方法
1.1 wait方法
我们可以在linux下输入man 2 wait来查看wait的用法 :::info #include<sys/types.h> #include<sys/wait.h> pid_t wait(int*status); 返回值: 成功返回被等待进程pid,失败返回-1。 参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL ::: 代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t id =fork();
if(id==0)
{
int cnt=5;
//子进程
while(--cnt)
{
printf("我是子进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
sleep(1);
}
// exit(0); //进程退出
}
sleep(10);
pid_t ret=wait(NULL);
if(id>0)
{
printf("wait success\n");
}
sleep(5);
return 0;
}
通过这样的操作我们发现子进程从僵尸状态下被回收了
1.2 waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options); 返回值: 当正常返回的时候waitpid返回收集到的子进程的进程ID; 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0; 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在; 参数: pid: pid=-1,等待任一个子进程。与wait等效。 Pid>0.等待其进程ID与pid相等的子进程。 status: WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码) options: WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进 程的ID。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t id =fork();
if(id==0)
{
int cnt=5;
//子进程
while(--cnt)
{
printf("我是子进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
sleep(1);
}
// exit(10); //进程退出
}
int status=0;
//这里的0表示阻塞式等待
pid_t ret=waitpid(id,&status,0);
if(ret>0)
{
printf("wait success:%d,ret:%d\n",ret,status);
}
return 0;
}
在我们输入这段代码以后理论上来说输出子进程的pid和退出码10
但是这个status和我们预想的不一样,原因在于
我们可以这样解决:
printf("wait success:%d,sig number:%d,child exit code:%d\n",ret,status&0x7F,(status>>8)&0xFF);
运行以后有如下结果:
关于信号的意义我们可以使用kill -l去查看其意义
我们可以通过这样的图去理解其内部意义
有心的读者可能会想在这期间父进程在干嘛,答案是父进程一直等待子进程结束,他什么事情都不干,也称之为阻塞式等待,如果我们想让父进程再次期间做事情,我们就需要非阻塞式等待
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t id =fork();
if(id==0)
{
int cnt=5;
//子进程
while(--cnt)
{
printf("我是子进程,我的pid是%d,我的ppid是%d\n",getpid(),getppid());
sleep(1);
}
exit(0); //进程退出
}
int status=0;
//这里的WNOHANG表示非阻塞式等待
while(1)
{
pid_t ret=waitpid(id,&status,WNOHANG);
if(ret==0)
{
sleep(1);
//调用成功&&子进程没有退出
//可以让子进程做别的事情
printf("我正在做别的事情\n");
}
if(ret>0)
{
//调用成功&&子进程退出了
printf("wait success:%d,sig number:%d,child exit code:%d\n",ret,status&0x7F,(status>>8)&0xFF);
//printf("wait success:%d,child exit code:%d\n",ret,WIFEXITED(status));
break;
}
else
{
//调用失败
printf("wait failed");
break;
}
}
return 0;
}
这样我们就可以实现双执行流