Process & Thread · Section 2
wait4(2)
Wait for a child process to change state and collect its exit information and resource usage.
Signature
#include <sys/wait.h>
#include <sys/resource.h>
pid_t wait4(pid_t pid, int * wstatus, int options, struct rusage * rusage);- pid
- Target. > 0: specific child PID. 0: any child in the caller's process group. -1: any child. < -1: child in process group |pid|.
- wstatus
- If non-NULL, receives the encoded exit status. Decode with WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG, WCOREDUMP.
- options
- Bitwise OR of WNOHANG (don't block), WUNTRACED (also report stopped), WCONTINUED (also report continued), __WCLONE / __WALL (control which children are eligible — relevant for ptrace-tracees and clone() children).
- rusage
- If non-NULL, receives the child's resource usage. NULL is fine when only the exit status is needed.
Description
wait4() blocks the caller until a child process changes state — exits, is killed by a signal, or (with WUNTRACED / WCONTINUED) stops or resumes. It returns the PID of the affected child and, via wstatus, an integer encoding the cause (decoded with WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG, etc.). If rusage is non-NULL, the child's CPU time and memory usage are returned in *rusage. pid > 0 waits for that specific child; pid == 0 waits for any child in the caller's process group; pid == -1 waits for any child; pid < -1 waits for any child in process group |pid|. wait4() is the canonical kernel mechanism that turns zombies into reaped exit notifications. wait() and waitpid() are libc wrappers; waitid() is the modern, more flexible alternative.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 114 | i386 | sys_wait4 |
| x64 (x86_64) | 61 | common | sys_wait4 |
| ARM64 (aarch64) | 260 | — | — |
Kernel history
Introduced in Linux 1.0.
1.0
wait4() has been part of Linux since 1.0, inherited from BSD as a superset of POSIX wait() (BSD added the rusage argument).
2.6.9
waitid() was added as a more capable variant with finer-grained idtype (P_PID, P_PGID, P_ALL, P_PIDFD) and richer siginfo_t output — the modern replacement, though wait4() is still ubiquitous because glibc's waitpid() uses it under the hood.
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
wait4() and waitid() are allowed by Docker / Podman default profiles. Blocking them is rarely useful — every process that spawns children needs to reap them or the kernel accumulates zombies indefinitely (until the parent dies and they reparent to init).
libseccomp
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(wait4), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(waitid), 0);strace example
$ strace -f -e wait4 bash -c 'sleep 0.1 &; wait'
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 13284
+++ exited with 0 +++wait4() in strace shows the decoded WIF* macros: '{WIFEXITED(s) && WEXITSTATUS(s) == 42}' or '{WIFSIGNALED(s) && WTERMSIG(s) == SIGSEGV}'. The WNOHANG return of 0 is normal in poll-loops; persistent ECHILD with active children suggests a SIGCHLD-to-SIG_IGN footgun.
Security & observability
wait4() is not directly a security primitive but its presence is part of the lifecycle: a process that fork()/execve()s children but never wait4()s them leaves zombies in /proc — a leak signature. Container init processes (tini, dumb-init, Pod's pause) exist primarily to reap orphaned children; without one, defunct entries pile up. eBPF tracepoint sched_process_exit covers the same lifecycle event from the kernel side and is preferred for monitoring because it doesn't rely on the parent calling wait4().
Errors
- ECHILD
- No matching children exist — either there are none, or they were all reaped already, or the caller has set SIGCHLD to SIG_IGN (which auto-reaps).
- EINTR
- Interrupted by a signal that did not have SA_RESTART set.
- EINVAL
- —
Flags
- WNOHANG
- 1
- Return 0 immediately if no child has changed state, instead of blocking. The polling-loop idiom.
- WUNTRACED
- 2
- Also report children that have stopped (e.g. via SIGSTOP). Used by debuggers and job-control shells.
- WCONTINUED
- 8
- Also report children that have resumed after being stopped (SIGCONT).
- __WCLONE
- 0x80000000
- —
- __WALL
- 0x40000000
- Wait for any child regardless of clone semantics (default excludes some kernel-thread-like clones). Used by strace and gdb.