好的,我们来深入学习 waitid 系统调用
1. 函数介绍
在 Linux 系统中,当一个进程创建了子进程(使用 fork),父进程通常需要知道子进程何时结束(退出或被终止),以及它是如何结束的(正常退出码、被哪个信号杀死等)。这是进程管理和资源回收的重要环节。
data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">waitid 系统调用就是用来让父进程(或具有适当权限的进程)等待一个或一组子进程的状态发生变化,并获取该变化的详细信息。
你可以把它想象成一个“进程状态监听器”。父进程调用 waitid 后,它会挂起(阻塞),直到它感兴趣的子进程发生了指定类型的事件(比如退出、被信号终止、停止、继续等)。当事件发生时,waitid 会返回,并把详细信息(哪个子进程、如何结束的)填充到一个结构体中。
waitid 相比于老一些的 wait 和 waitpid,提供了更强大和灵活的功能。
简单来说,waitid 就是让你用程序来“等待”并“获取”子进程的“死亡/停止/恢复”通知书,并且通知书上写得非常详细。
2. 函数原型
1 | #include <sys/wait.h> // 包含 waitid 函数声明和相关常量 |
3. 功能
挂起调用进程,直到由 idtype 和 id 指定的一个或多个子进程的状态发生变化(变化类型由 options 指定)。当满足条件的子进程状态改变时,将详细的状态信息填充到 infop 指向的 siginfo_t 结构体中。
4. 参数详解
idtype:
- idtype_t 类型。
指定要等待的进程的类型。它决定了 id 参数的含义。常见的值有:
P_PID: 等待由 id 指定的特定进程 ID (PID) 的子进程。
P_PGID: 等待进程组 ID (PGID) 等于 id 的所有子进程。
P_ALL: 等待调用进程的所有子进程(此时 id 参数被忽略)。
id:
- id_t 类型。
其含义由 idtype 决定:
如果 idtype 是 P_PID,则 id 是要等待的子进程的 PID。
如果 idtype 是 P_PGID,则 id 是要等待的子进程组的 PGID。
如果 idtype 是 P_ALL,则 id 被忽略(通常设为 0)。
infop:
siginfo_t * 类型。
一个指向 siginfo_t 结构体的指针。当 waitid 成功返回时,该结构体会被内核填充为关于已改变状态的子进程的详细信息。
siginfo_t 结构体包含很多字段,关键的有:
si_pid: 导致状态改变的子进程的 PID。
si_status: 子进程的退出状态或导致其状态改变的信号编号。
si_code: 状态改变的原因代码,例如:
CLD_EXITED: 子进程通过 exit() 或从 main 返回正常退出。
CLD_KILLED: 子进程被信号杀死。
CLD_DUMPED: 子进程被信号杀死并产生了核心转储 (core dump)。
CLD_STOPPED: 子进程被信号(如 SIGSTOP)停止。
CLD_CONTINUED: 子进程从停止状态被 SIGCONT 信号恢复继续运行。
… 还有其他字段。
options:
- int 类型。
一个位掩码,用于指定要等待的状态变化类型以及调用的行为。可以是以下值的按位或 (|) 组合:
状态类型 (必须至少指定一个):
WEXITED: 等待子进程正常退出(调用 exit() 或从 main 返回)。
WSTOPPED: 等待子进程被停止(通常是收到 SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU 信号)。
WCONTINUED: 等待被停止的子进程恢复运行(收到 SIGCONT 信号)。
行为标志 (可选):
WNOHANG: 非阻塞。如果没有任何子进程的状态符合条件,waitid 立即返回 0,而不挂起调用进程。
WNOWAIT: 不收割。获取子进程状态信息,但不将其从内核的子进程表中删除。这意味着后续的 wait 调用仍可能获取到该子进程的信息。
5. 返回值
成功: 返回 0。
失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。
6. 错误码 (errno)
ECHILD: 没有符合条件的子进程。例如,指定了一个不存在的 PID,或者使用 WNOHANG 时没有子进程处于可收割状态。
EINTR: 系统调用被信号中断。
EINVAL: idtype 或 options 参数无效。
7. 相似函数或关联函数
wait: 最基础的等待子进程退出的函数。它等待任意一个子进程退出,并返回 PID 和状态码(需要使用宏如 WIFEXITED, WEXITSTATUS 等来解析)。pid_t wait(int *wstatus);
waitpid: wait 的增强版。允许指定等待特定 PID 的子进程,或使用 WNOHANG 等选项。pid_t waitpid(pid_t pid, int *wstatus, int options);
wait3 / wait4: 更老的函数,功能与 waitpid 类似,但可以额外返回资源使用信息(struct rusage)。
siginfo_t: waitid 使用的关键数据结构,包含详细的子进程状态信息。
8. 示例代码
下面的示例演示了如何使用 waitid 来等待不同类型的子进程事件。
1 | #define _GNU_SOURCE // 启用 GNU 扩展 |
9. 编译和运行
1 | # 假设代码保存在 waitid_example.c 中 |
10. 预期输出
1 | --- Demonstrating waitid --- |
11. 总结
waitid 是一个功能强大且信息丰富的系统调用,用于等待子进程状态变化。
核心优势:
灵活性高:可以精确指定等待哪个进程/进程组,以及等待哪种类型的事件(退出、停止、恢复)。
信息详细:通过 siginfo_t 结构体返回非常详细的子进程状态信息,比 wait/waitpid 的 wstatus 整数更易于理解和使用。
功能完整:支持停止/恢复事件的等待(WSTOPPED, WCONTINUED)。
参数:idtype/id 定义范围,options 定义事件类型和行为,infop 接收结果。
使用场景:
需要精确控制等待哪个子进程。
需要区分子进程是正常退出、被信号杀死还是停止/恢复。
编写复杂的进程管理器或守护进程。
与 wait/waitpid 的关系:
wait(&status) 基本等价于 waitpid(-1, &status, 0)。
waitpid(pid, &status, options) 功能是 waitid 的子集。
waitid 提供了 waitpid 所没有的 WSTOPPED/WCONTINUED 等选项(除非使用非标准扩展),以及更详细的信息返回方式。