wait和waitpid函数
在linux操作系统中,进程在创建的时候操作系统会给进程分配一个进程控制块,这个进程控制块结构体里面描述了进程的关键信息,在进程结束后操作系统会将进程控制块回收,释放进程控制块占用的内存资源,而这个回收工作通常是由父进程来完成。父进程回收子进程的时候需要借助wait和waitpid函数完成。
函数介绍
//包含的头文件
#include <sys/types.h>
#include <sys/wait.h>
//函数原型
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
wait函数作用:
- 阻塞等待任一子进程退出,也就是说父进程如果调用wait函数,就会一直阻塞等待子进程退出再返回
- 回收子进程的残留资源
- 获取子进程退出的状态
wait函数参数:
- status,该参数是一个输出型参数,如果传参是一个有效的int型指针,那么在该函数返回的时候,参数中保存了子进程的退出状态,如果程序不关心子进程的退出状态可以直接传入NULL。
wait返回值:
- 成功,返回大于零的整数,被回收的子进程ID;
- 失败,返回-1,设置errno。这种情况通常是所有的子进程都退出了,没有子进程可回收。
waitpid函数作用:
- 回收子进程的残留资源
- 获取子进程退出的状态
waitpid函数参数:
- pid,这个参数有四种取值方式; (1)大于0,表示要回收的子进程ID;(2)等于-1,回收任一子进程,和wait函数效果相同,无差别回收;(3)等于0,回收和父进程同组的任一子进程;(4)小于-1,回收以该参数绝对值为组ID的同组任一子进程。
- status,该参数是一个输出型参数,如果传参是一个有效的int型指针,那么在该函数返回的时候,参数中保存了子进程的退出状态,如果程序不关心子进程的退出状态可以直接传入NULL。
- options,指定函数的运行方式,如果传0,则调用该函数时阻塞等待子进程退出;如果传WNOHANG则调用时不发生阻塞,立马返回。
waitpid返回值:
- 大于0,返回被回收的子进程ID;
- 等于0,同时参数3设置为WNOHANG,表明被回收的子进程未结束
- 失败,返回-1,设置errno。这种情况通常是所有的子进程都退出了,没有子进程可回收。
进程退出状态
进程退出可以是执行完代码正常退出,也可以是由于异常导致退出,比如:被信号杀死。进程的退出状态父进程在调用wait或者waitpid函数通过status参数就可以获得。具体进程是由于那种原因退出,linux操作系统提供了以下宏函数
WIFEXITED(status) //判断进程是否是正常退出
WEXITSTATUS(status) //如果进程正常退出,获取进程退出值
WIFSIGNALED(status) //判断进程是否是被信号终止
WTERMSIG(status) //如果进程是被信号终止,获取信号值
示例
- process_wait.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv)
{
int status = 0;
int pid = fork();
if(pid > 0) //父进程
{
wait(&status); //阻塞等待子进程退出
if(WIFSIGNALED(status)) //进程被信号终止
{
//打印杀死子进程的信号值
printf("child killed by signal %d\n", WTERMSIG(status));
}
else if(WIFEXITED(status)) //进程正常退出
{
//打印子进程退出值
printf("child exit number: %d\n", WEXITSTATUS(status));
}
}
else if(pid == 0) //子进程
{
printf("I’am child process and my ID is:%d\n", getpid());
sleep(20);
return 100;
}
else
{
perror("fork error");
exit(1);
}
return 0;
}
- process_waitpid.c
int main(int argc, char ** argv)
{
int status = 0;
int pid = fork();
if(pid > 0) //父进程
{
while(waitpid(pid, &status, WNOHANG) == 0); //非阻塞等待子进程退出
if(WIFSIGNALED(status))
{
//打印杀死子进程的信号值
printf("child killed by signal %d\n", WTERMSIG(status));
}
else if(WIFEXITED(status))
{
//打印子进程退出值
printf("child exit number: %d\n", WEXITSTATUS(status));
}
}
else if(pid == 0) //子进程
{
printf("I’am child process and my ID is:%d\n", getpid());
sleep(20);
return 100;
}
else
{
perror("fork error");
exit(1);
}
return 0;
}
wait和waitpid函数的注意事项:
- 这两个函数只能用于父进程回收子进程
- 一次调用只回收一个子进程,如果想回收多个子进程则需要多次调用
- 当waitpid函数设置了非阻塞模式,那么调用不一定能够回收到子进程,如果要确保能够回收子进程则可以设置阻塞方式回收或者使用轮询模式