一、前言
本文将介绍在unix系统中,父进程如何获取子进程的终止状态。本文主要围绕如下函数展开:
1.wait
2.waitpid
3.waitid
4.wait3、wait4
在讨论这些函数前,先介绍一个进程从创建到释放子进程的过程。
二、子进程的创建以及终止
在unix系统中,一个进程可以通过fork()函数来创建一个新的进程。如果父进程先终止,那么子进程就会成为孤儿进程,然后被init进程收留,成为init进程的子进程。如果子进程先终止,那么内核就会向父进程发送一个SIGCHLD的信号,父进程默认忽略该信号。如果子进程终止后,父进程没有去获取子进程的终止状态,那么子进程就会在内存保存一段信息,成为僵死进程。
父进程通过wait族函数来获取子进程的终止状态。
三、wait族函数介绍
wait族函数的作用是获取子进程的终止状态。下面将详细介绍每个函数:
3.1 wait
#include <sys/wait.h> pid_t wait(int *status);
参数:用于存储子进程的退出状态,如果不关心状态,可以传入NULL 返回值:成功返回子进程的PID,出错返回0或-1
调用wait函数会有如下三种情况:
1.如果所有的子进程都在运行中,则父进程阻塞
2.如果一个子进程已经终止,正等待父进程获取其状态,则返回该子进程的PID
3.如果没有任何子进程,则立即报错返回
unix通过四个宏来获取子进程的退出状态,如下:
- WIFEXITED(status):用于判断子进程是否正常退出。如果是则返回真。
- WEXITSTATUS(status):获取正常退出的子进程的返回值。
- WIFSIGNALED(status):判断子进程是否因信号而终止。如果是则返回真。
- WTERMSIG(status):获取导致子进程终止的信号号码
- WIFSTOPPED(status):检查子进程是否被停止。
3.2 waitpid
waitpid的功能比wait更为丰富,它能指定等待某个特定的子进程结束,且能够选择是否阻塞。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>pid_t waitpid(pid_t pid, int *status, int options);
参数:
pid:要等待的子进程的PID。如果是正数,则等待对应的子进程;如果是-1,则等待任意子进程;如果是0,则等待与调用进程同组的任意子进程。
status:用于存储子进程的退出状态
options:控制函数行为选项,比如WNOHANG(非阻塞等待)。返回值:成功的话返回被等待的子进程PID,没有匹配的子进程返回0,错误返回-1
3.3 waitid
waitid的功能和waitpid类似,只不过在传入参数上有些许差异。
#include <sys/types.h>
#include <sys/wait.h>int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
参入参数:
idtype: 指定要等待的进程 ID 类型,可以是以下值之一: P_PID: 等待指定进程的终止。
P_PGID: 等待指定进程组的终止。
P_ALL: 等待所有子进程的终止。
id: 取决于 idtype 的值。它可以是某个特定进程的ID,或是某个进程组的 ID,或是 0(针对所有子进程)。
infop: 是一个指向 siginfo_t结构体的指针,用于接收有关终止子进程的状态信息。
options: 控制函数行为的选项,使用方式类似于waitpid返回值:成功时返回0,出错返回-1
ps:需要注意的是WCONTINUE、WEXITED、WSTOPPED这三个参数之一必须在options中指定。
3.4 wait3、wait4
wait3和wait4和前面几个函数相比主要多了一个获取终止进程及其子进程所使用的系统资源的功能。
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <unistd.h>pid_t wait3(int *status, int options, struct rusage *rusage);
参数:
status:用于返回子进程的退出状态
options:控制调用的行为
rusage:指向 struct rusage的指针,用于获取子进程的资源使用情况
返回值:成功返回被等待的子进程的PID,失败返回-1========================================
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
wait4与wait3相比就增加了可以选择等待特定的子进程
struct rusage数据结构的定义如下:
#include <sys/resource.h>
struct rusage {
struct timeval ru_utime; // 用户 CPU 时间
struct timeval ru_stime; // 系统 CPU 时间
long ru_maxrss; // 最高常驻集大小(以 KB 为单位)
long ru_ixrss; // 增加的共享内存大小
long ru_idrss; // 增加的独占内存大小
long ru_isrss; // 增加的共享数据段大小
long ru_minflt; // 页面未命中数
long ru_majflt; // 页面错误数
long ru_nswap; // 交换区使用的次数
long ru_inblock; // 输入块数
long ru_oublock; // 输出块数
long ru_msgsnd; // 发送的消息数
long ru_msgrcv; // 接收的消息数
long ru_nsignals; // 发送的信号数
long ru_nvcsw; // 自愿上下文切换次数
long ru_nivcsw; // 非自愿上下文切换次数 };
四、参考代码
wait函数的参考代码如下:
/*************************************************************************
> File Name: wait_test.c
> Author: conbiao
> Created Time: 2024年09月25日 星期三 11时14分36秒
************************************************************************/
/***********************************************************************
* HEADER
**********************************************************************/
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/resource.h>
/***********************************************************************
* MACRO
**********************************************************************/
#define WAIT3
/***********************************************************************
* GLOBAL VARIABLE
**********************************************************************/
/***********************************************************************
* FUNCTION DESCRIPTION
**********************************************************************/
/***********************************************************************
* FUNCTION NAME:
***********************************************************************
*
* Summary:
*
* Params:
*
* Return:
*
***********************************************************************/
/***********************************************************************
* MAIN
**********************************************************************/
int main(int argc, char *argv[])
{
int ret = 0;
pid_t pid;
int status;
pid_t p;
pid = fork();
if(pid < 0)
{
printf("fork fail!\n");
}
else if(pid == 0)
{
printf("This is child process!\n");
#ifdef SIGNAL_EXIT
int x = 2 / 0;
#endif
sleep(2);
}
else
{
#ifdef WAIT
if((p = wait(&status))!= pid)
{
printf("wait child error! pid: %d. p: %d \n",pid,p);
}
else
{
printf("This is father process,child process: %d\n",pid);
}
#endif
#ifdef WAITPID
if((p = waitpid(pid,&status,WNOHANG)) == pid)
{
printf("This is father process,child process: %d\n",pid);
}
else
{
printf("p: %d\n",p);
}
#endif
#ifdef WAITID
siginfo_t info;
int res = waitid(P_PID,pid,&info,WEXITED);
if(!res)
{
printf("This is father process,child process:%d\n",pid);
}
else
{
printf("waitid fail! res: %d\n",res);
}
#endif
#ifdef WAIT3
struct rusage rus;
if(wait3(&status, 0 ,&rus) == pid)
{
printf("This is father process,pid: %d,systime: %ld.%06ld seconds\n",pid, \
(long)rus.ru_stime.tv_sec, (long)rus.ru_stime.tv_usec);
}
else
{
printf("wait3 fail!\n");
}
#endif
if(WIFEXITED(status))
{
printf("child process over nomally,exit code is: %d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("child process over by receive signal,signal is: %d\n",WTERMSIG(status));
}
}
return ret;
}
运行结果如下:
(4-1)
参考资料:
《UNIX环境高级编程(第3版) (史蒂文斯 (W.Richard Stevens) 拉戈(StephenA.Rago))(Z-Library)》