目录
每个进程退出的时候,内核 释放该进程所有的资源、包括打来的文件、占用的内存等用户态数据,但是仍然为其保留一定的信息,这些信息主要是进程控制块PCB的信息(包括进程号、退出状态、运行时间等)。如果子进程退出,父进程不调用wait 或 waitpid 对子进程PCB资源释放,就会产生僵尸进程。
父进程可以通过 调用wait 或 waitpid得到子进程的退出状态,同时彻底清除掉子进程的资源。
wait() 和 waitpid()函数的功能一样,区别在于,wait()函数会阻塞,waitpid()可以设置非阻塞,waitpid() 还可以指定等待哪个子进程结束。
注意: 一次wait 或 waitpid 调用只能清理一个子进程,清理多个子进程应使用循环多次调用。
wait 和 waitpid 函数的使用和说明,可以通过 man 2 wait 、man 2 waitpid 查看相应的帮助文档。
一、 wait函数的使用:
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *wstatus);
功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收子进程
参数:int* wstatus
进程退出时的状态信息,传入的是一个int类型的地址,是传出参数。
返回值:
-成功:返回被回收的子进程的id
-失败:-1
调用wait函数的进程会被挂起(阻塞),直到它的一个子进程退出或者收到一个不能被忽略的信号时才被唤醒(相当于继续往下执行)
如果没有子进程了,函数立刻返回,返回-1;如果子进程都已经结束了,也会立即返回,返回-1,并且会回收子进程的资源。
代码示例 一:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
//有一个父进程,创建5个子进程(兄弟)
pid_t pid ;
/*
如果执行 :
fork();
fork();
这样会创建3个子进程,因为 主进程 执行到第一个 fork() ,会创建一个进程,然后这个创建的进程 又会创建一个子进程 。
主进程 执行到 第二个 fork的时候,又会创建一个进程。
*/
for(int i=0 ; i<5 ; ++i)
{
pid = fork();
//如果是子进程,不再对子进程,继续创建子进程的子进程
if(pid == 0)
break;
}
if(pid > 0)
{
//父进程
while(1)
{
printf("parent,pid=%d\n",getpid());
int st;
int ret;
ret = wait(&st);
if(ret == -1) break;
printf("child die,pid = %d\n",ret);
sleep(1);
}
}else if(pid == 0)
{//子进程
while (1)
{
printf("child,pid = %d\n",getpid());
sleep(1);
}
}
return 0;
}
执行结果:
从以上执行结果可以看到, parent,pid = 3177 只执行了一次,也就验证了 wait 是阻塞的。 父进程打印完parent,pid = 3177,执行到wait 会阻塞,直到 其子进程调用exit结束进程,wait会回收子进程的资源。 并且会继续往wait()后面执行,后面的程序。
代码示例二如下:
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
//有一个父进程,创建5个子进程(兄弟)
pid_t pid ;
for(int i=0 ; i<5 ; ++i)
{
pid = fork();
//如果是子进程,不再对子进程,继续创建子进程的子进程
if(pid == 0)
break;
}
if(pid > 0)
{
//父进程
while(1)
{
printf("parent,pid=%d\n",getpid());
int st;
int ret;
ret = wait(&st);
if(ret == -1) break;
if(WIFEXITED(st))
{
//是不是正常退出
printf("退出的状态码:%d\n",WEXITSTATUS(st));
}
if(WIFSIGNALED(st))
{
//是不是异常终止
printf("被哪个信号干掉了:%d\n",WTERMSIG(st));
}
printf("child die,pid = %d\n",ret);
sleep(1);
}
}else if(pid == 0)
{//子进程
printf("child,pid = %d\n",getpid());
sleep(1);
exit(0);
}
return 0;
}
执行结果如下:
五个子进程,会逐个的调用exit() 结束进程 ,父进程通过调用wait()回收子进程的资源,直到所有的子进程都结束了,父进程调用wait() 会返回-1, 因此父进程也会结束进程。
二、waitpid 函数的使用
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid( pid_t pid, int* wstatus, int options);
功能:回收指定进程号的子进程,可以设置是否阻塞。
参数:
-pid:
pid>0: 某个子进程的pid
pid=0: 回收当前进程组的所有子进程
pid = -1: 回收所有的子进程
pid<-1: 回收进程组的ID 与 pid 的绝对值相等
A 产生了进程B,C,D,则 进程组的ID 就是 进程A的ID
-options: 设置阻塞或者非阻塞
0:阻塞
WNOHANG:非阻塞
-返回值:
>0: 返回子进程的id
=0:options = WNOHANG时,表示还有子进程活着
= -1:错误,或者没有子进程了
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
//有一个父进程,创建5个子进程(兄弟)
pid_t pid ;
for(int i=0 ; i<5 ; ++i)
{
pid = fork();
//如果是子进程,不再对子进程,继续创建子进程的子进程
if(pid == 0)
break;
}
if(pid > 0)
{
//父进程
while(1)
{
printf("parent,pid=%d\n",getpid());
sleep(1);
int st;
//int ret = waitpid(-1,&st,0); 阻塞状态,等同于wait调用
int ret = waitpid(-1,&st,WNOHANG);
if(ret == -1) break;
else if(ret == 0)
{
continue;//说明还有子进程存在
}
else if(ret >0){
if(WIFEXITED(st))
{
//是不是正常退出
printf("退出的状态码:%d\n",WEXITSTATUS(st));
}
if(WIFSIGNALED(st))
{
//是不是异常终止
printf("被哪个信号干掉了:%d\n",WTERMSIG(st));
}
printf("child die,pid = %d\n",ret);
}
}
}else if(pid == 0)
{//子进程
while (1)
{
printf("child,pid = %d\n",getpid());
sleep(1);
}
}
return 0;
}
执行结果: