waitpid(2)
System call
Waits for a child process to end and collects its status: exit code or the signal that killed it. Without this call, the dead child stays a zombie.
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);Description
waitpid suspends the calling process until one of its children changes state, which in practice means until it terminates. It then releases what was left of the child in the kernel: a terminated child that nobody has waited for yet is a zombie.
The pid parameter picks whom to wait for: a positive PID targets that specific child, -1 accepts any child. wait(&status) is exactly waitpid(-1, &status, 0). The last parameter takes options, the most useful being WNOHANG, which returns right away if no child has finished.
If wstatus is not NULL, waitpid stores an encoded integer there, which you read with these macros:
| Macro | Meaning |
|---|---|
WIFEXITED(s) | true if the child ended normally (exit, return from main) |
WEXITSTATUS(s) | its exit code, the low 8 bits of the value passed to exit |
WIFSIGNALED(s) | true if a signal killed the child |
WTERMSIG(s) | the number of that signal |
Return value
The PID of the child whose state changed. With WNOHANG, 0 if the targeted children exist but have not changed state yet. On error, -1 and errno: ECHILD when there is no child to wait for, EINTR when a signal interrupted the wait.
Pitfalls
WEXITSTATUSonly makes sense ifWIFEXITEDis true, andWTERMSIGonly ifWIFSIGNALEDis true. Always test the first macro.The exit code is cut down to 8 bits:
exit(256)reads as 0,exit(-1)as 255.A child can be waited for only once. A second
waitpidon the same PID fails withECHILD.
Example
#include <stdio.h>#include <stdlib.h>#include <sys/wait.h>#include <unistd.h>
static void report(pid_t pid){ int status;
if (waitpid(pid, &status, 0) == -1) { perror("waitpid"); return; } if (WIFEXITED(status)) printf("%d: exited, code %d\n", pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf("%d: killed by signal %d\n", pid, WTERMSIG(status));}
int main(void){ pid_t a = fork(); if (a == 0) exit(256);
pid_t b = fork(); if (b == 0) abort();
report(a); report(b); report(b); return 0;}$ gcc -Wall -Wextra status.c -o status$ ./status15: exited, code 016: killed by signal 6waitpid: No child processesThe first child asked for 256 and the parent reads 0. The second one is killed by SIGABRT, signal 6, which abort raises. The third call targets a child that was already waited for, hence ECHILD.
signal_watchdog
Write signal_watchdog, which runs a function in a child, reads how it ended from its status, and leaves no zombie behind, even after a timeout.