Chapter 26 — Part 1: The wait() System Call

Chapter 26 — Part 1: The wait() System Call
Linux Process Monitoring · Monitoring Child Processes Series
Topic
wait() Basics
Level
Intermediate
Examples
3 Programs

What is wait()?

In Linux, when a parent process creates child processes using fork(), the parent often needs to know when and how each child terminates. The wait() system call is the fundamental mechanism for this.

Calling wait() from a parent process does four important things:

  • Blocks until one of its children terminates (or returns immediately if one already has)
  • Returns the termination status of the child via the status pointer
  • Adds the child’s CPU time and resource stats to the parent’s running totals
  • Returns the PID of the terminated child as the function result

Function Signature
#include <sys/wait.h>

pid_t wait(int *status);
/* Returns PID of terminated child, or -1 on error */

status — pointer to an int where termination info is stored. Pass NULL if you don’t care about the status.

How wait() Works — Visual Flow
Parent Process calls wait()
Any child already terminated?
YES ✓
NO ✗
Returns immediately
with child PID
Blocks (sleeps) until
a child terminates
Fills *status, returns child PID · Kernel frees zombie

Example 1: Basic wait() Usage

A parent creates one child, waits for it, and prints the child’s PID.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid, child_pid;
    int status;

    pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        /* Child process */
        printf("[Child] PID=%d, doing some work...\n", getpid());
        sleep(2);
        printf("[Child] Exiting with status 0\n");
        exit(0);
    }

    /* Parent process */
    printf("[Parent] PID=%d, waiting for child PID=%d\n", getpid(), pid);

    child_pid = wait(&status);

    if (child_pid == -1) {
        perror("wait");
        exit(EXIT_FAILURE);
    }

    printf("[Parent] Child PID=%d terminated\n", child_pid);

    if (WIFEXITED(status))
        printf("[Parent] Child exit status: %d\n", WEXITSTATUS(status));

    return 0;
}

Expected output:

[Parent] PID=1001, waiting for child PID=1002
[Child] PID=1002, doing some work...
[Child] Exiting with status 0
[Parent] Child PID=1002 terminated
[Parent] Child exit status: 0

Example 2: Waiting for Multiple Children

A parent creates N children and uses a loop to wait for all of them. The loop exits when wait() returns -1 with errno == ECHILD (no more children).

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int num_children = 3;
    int sleep_times[] = {3, 1, 2};  /* Each child sleeps different durations */
    pid_t child_pid;
    int num_dead = 0;

    /* Create multiple children */
    for (int j = 0; j < num_children; j++) {
        switch (fork()) {
        case -1:
            perror("fork");
            exit(EXIT_FAILURE);

        case 0:
            /* Child: sleep then exit */
            printf("[Child %d] PID=%d, sleeping %d seconds\n",
                   j + 1, getpid(), sleep_times[j]);
            sleep(sleep_times[j]);
            printf("[Child %d] PID=%d exiting\n", j + 1, getpid());
            _exit(EXIT_SUCCESS);

        default:
            /* Parent: continue creating children */
            break;
        }
    }

    /* Parent waits for all children */
    for (;;) {
        child_pid = wait(NULL);  /* NULL = don't care about status */

        if (child_pid == -1) {
            if (errno == ECHILD) {
                /* No more children left */
                printf("[Parent] No more children. Total reaped: %d\n", num_dead);
                break;
            } else {
                perror("wait");
                exit(EXIT_FAILURE);
            }
        }

        num_dead++;
        printf("[Parent] Reaped child PID=%d (total dead=%d)\n",
               child_pid, num_dead);
    }

    return 0;
}

Key point: Children finish in order of their sleep time (1 sec, 2 sec, 3 sec), not creation order.

Example 3: Error Handling — No Children

Calling wait() when a process has no children returns -1 with errno = ECHILD.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>

int main(void)
{
    pid_t result;

    /* Calling wait() with no children */
    result = wait(NULL);

    if (result == -1) {
        if (errno == ECHILD)
            printf("wait() failed: No children to wait for (ECHILD)\n");
        else
            perror("wait");
    }

    return 0;
}
/* Output: wait() failed: No children to wait for (ECHILD) */

Limitations of wait()
Limitation Problem Solution
Cannot wait for specific child Waits for ANY child Use waitpid(pid,…)
Always blocks No non-blocking option Use waitpid() with WNOHANG
Only terminal events Can’t detect stopped children Use waitpid() with WUNTRACED

Key Terms:

wait() ECHILD pid_t WIFEXITED WEXITSTATUS zombie fork() _exit()

Interview Questions

Q1. What does wait() return and what does -1 indicate?

It returns the PID of the terminated child on success. A return value of -1 indicates an error — typically ECHILD meaning no (unwaited) children exist.

Q2. What happens if you call wait() before any child has terminated?

The calling process blocks (sleeps) until one of its children terminates.

Q3. What is the difference between passing NULL vs a valid pointer to wait()?

Passing NULL means you don’t want termination status. Passing a valid int* gives you the status which you can inspect using macros like WIFEXITED().

Q4. Can a parent use wait() to wait for a specific child?

No. wait() waits for ANY child. To wait for a specific child, use waitpid(pid, ...).

Q5. What does _exit() do differently from exit()?

_exit() terminates immediately without flushing stdio buffers or calling atexit() handlers. Child processes should use _exit() to avoid double-flushing the parent’s stdio buffers.

Q6. Why does the kernel accumulate child CPU times when wait() is called?

So the parent can retrieve cumulative resource usage of all waited-for children via getrusage(RUSAGE_CHILDREN, ...).

Q7. Write the loop idiom to wait for ALL children of a process.

while ((child_pid = wait(NULL)) != -1)
    continue;
if (errno != ECHILD)
    perror("wait"); /* unexpected error */

Q8. Is the order in which wait() reaps multiple terminated children guaranteed?

No. SUSv3 leaves the order unspecified, and it can vary even across Linux kernel versions.

Next: waitpid() — More Control Over Child Monitoring

Learn how to wait for a specific child, use non-blocking waits, and detect stopped children.

Part 2: waitpid() → EmbeddedPathashala Home

Leave a Reply

Your email address will not be published. Required fields are marked *