一.进程等待的必要性
1.如果子进程退出,父亲还在继续执行而且不接收子进程的退出信息,则子进程就会变成僵尸进程。
2.僵尸进程有很多的危害,会造成内存泄露等;
3.僵尸进程无法通过kill -9杀死(因为僵尸进程已经死了,没有办法杀死已经死了的进程);
4.父进程创建子进程会让子进程执行一些任务,我们必须要知道子进程将任务执行的如何,子进程是否正常退出。
5.所以父进程通过进程等待的方式回收子进程的资源,获取子进程的退出信息。
二.进程等待的方式(进程等待有多种方式,主讲下面两种)
1.wait(wait为阻塞等待)
2.waitpid(当waitpid的第三个参数为0时表示阻塞等待,当为WNOHANG时表示非阻塞等待)
注:阻塞等待:一直检测某个条件,条件不满足也要一直等;非阻塞等待:隔一段时间检测某个条件,不满足可以做别的事情。
三.进程等待的方式之wait
1.wait函数原型为:
pid_t wait(int* status)
2.返回值
成功返回被等待进程的pid,失败返回-1
3.参数
(1)输出型参数,用于获取子进程的退出信息,如果不关心子进程的退出信息,则可将参数设置为NULL。
(2)获取子进程的status
①status的低8位用于存放信号信息,当低8位为0时,表示进程正常退出,正常退出就会有退出码,则退出码用次低8位(低8位前面的8位)表示。
②当低8位不为0时,说明该进程异常退出(被信号打断),此时低8位用于存放异常终止信号。
(3)所以调用wait时,先看status的低8位,低8位为0时才看次低8位。
4.测试代码
(1)不关心退出状态(参数为NULL)
#include<stdio.h>
#include<unistd.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == 0)
{
//child
printf("child pid=%d\n",getpid());
sleep(20); //子进程睡20秒之后再退出,在这20秒内父进程会一直等待
exit(1);
}
else if(pid > 0)
{
//father
printf("father\n");
pid_t ret = wait(NULL);
printf("exitnum %d\n",ret); //ret为wait的返回值,成功返回子进程的pid
}
else
{
perror("fork()");
}
return 0;
}
运行结果:(刚开始会打印出father和child pid这些内容,过20秒之后会输出exitnum这句话)

(2)参数不为空(即关心子进程的退出状态)
#include<stdio.h>
#include<unistd.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork()\n");
}
else if(pid == 0)
{
//child
printf("child pid:%d\n",getpid());
sleep(20);
exit(10);
}
else
{
//father
int status;
pid_t ret = wait(&status);
if(ret < 0 )
{
perror("wait()\n");
}
else
{
if((status & 0X7F) == 0)
{//正常退出
printf("正常退出,child exit code:%d\n",(status>>8)&0XFF);
else
{
printf("异常退出,sig code:%d\n",status & 0x7F);
}
}
}
return 0;
}
- 运行结果:(20秒之后会输出下面的内容)

- 如果在别的终端用信号来终止这个进程(如下利用9号信号来终止进程),运行结果如下:


四.进程等待的方式之waitpid
(1)waitpid函数的原型:
pid_t waitpid(pid_t pid, int* status, int options)
(2)返回值:
正常返回时,返回收集到的子进程的pid;当第三个参数设置为WNOHANG,当调用者发现没有没有已退出的子进程可收集(或者子进程还在运行),则返回0;如果调用出错,返回-1.
(3)参数:
①pid:
- pid=-1,表示等待任意一个子进程;
- pid>0表示等待指定的进程(其子进程的ID与pid相等的进程,由fork的返回值知道其子进程的pid)
②status:(与wait的参数status的用法一致)
还可以通过一些宏来获得:
- WIFEXITED(status):若为真,表示进程正常退出(用于查看信号)
- WEXITSTATUS(status):当WIFEXITED(status)为真时查看进程的退出码
③options:
- 为0表示阻塞式等待,与wait的作用一致;
- 为WNOHANG表示非阻塞等待
(4)测试代码:
①当waitpid的第三个参数为0时表示阻塞等待
#include<stdio.h>
#include<unistd.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork()\n");
}
else if(pid == 0)
{//child
printf("child , pid = %d\n",getpid());
sleep(5);
exit(35);
}
else
{//parent
int status;
pid_t ret = waitpid(-1,&status,0); //阻塞式等待,-1表示等待任意一个进程
if(ret == -1)
{
perror("wait()");
}
else
{
if(WIFEXITED(status))
{//正常退出
printf("正常退出,child exit code:%d\n",WEXITSTATUS(status));
}
else
{
printf("异常退出\n");
}
}
}
return 0;
}
运行结果:

- 当子进程被信号所终止时,运行结果如下:


②非阻塞等待的测试代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<wait.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();
if(pid == -1)
{
perror("fork()");
}
else if(pid == 0)
{//child
printf("child, pid = %d\n",getpid());
sleep(5);
exit(60);
}
else
{//parent
int status;
pid_t ret =0;
do
{
ret = waitpid(-1,&status,WNOHANG); //非阻塞式等待
if(ret == 0) //说明子进程还在运行
{
printf("child is running\n");
}
sleep(1);
}while(ret == 0);
//此时说明ret != 0
if(WIFEXITED(status))
{
printf("child exit code:%d\n",WEXITSTATUS(status));
}
else
{
printf("sig code is %d\n",status & 0x7F);
}
}
return 0;
}
运行结果:

- 当子进程被信号所异常终止时,运行结果如下:

