Chapter 26 — Part 2: The waitpid() System Call

Chapter 26 — Part 2: The waitpid() System Call
Selective, Non-Blocking, and Advanced Child Monitoring
Topic
waitpid()
Level
Intermediate
Examples
3 Programs

Why waitpid()?

wait() has three big limitations: it cannot target a specific child, it always blocks, and it cannot detect stopped children. waitpid() was designed to fix all three.

Function signature:

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
/* Returns PID of child, 0 (with WNOHANG), or -1 on error */

The pid Argument — Who to Wait For
pid value Meaning
pid > 0 Wait for child whose PID equals pid
pid == 0 Wait for any child in the same process group as the caller
pid == -1 Wait for ANY child (same as wait())
pid < -1 Wait for any child whose process group ID == abs(pid)

The options Argument — Flags
Flag Effect
WNOHANG Do not block. Return 0 immediately if no child has changed state.
WUNTRACED Also return when a child is stopped by a signal.
WCONTINUED Also return when a stopped child is resumed by SIGCONT. (Linux 2.6.10+)
0 (no flags) Block until child terminates (same as wait())

waitpid() Return Value Flow
waitpid(pid, &status, options)
Returns child PID
Child state changed
Returns 0
WNOHANG, no change yet
Returns -1
Error (e.g. ECHILD)

Example 1: Wait for a Specific Child

Create two children; parent waits only for the second one first.

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

int main(void)
{
    pid_t child1, child2, result;
    int status;

    /* Create first child — sleeps longer */
    child1 = fork();
    if (child1 == 0) {
        printf("[Child1] PID=%d sleeping 5s\n", getpid());
        sleep(5);
        _exit(10); /* exit status 10 */
    }

    /* Create second child — exits quickly */
    child2 = fork();
    if (child2 == 0) {
        printf("[Child2] PID=%d sleeping 1s\n", getpid());
        sleep(1);
        _exit(20); /* exit status 20 */
    }

    printf("[Parent] Waiting specifically for Child2 (PID=%d)\n", child2);

    /* Wait only for child2 */
    result = waitpid(child2, &status, 0);
    if (result == child2 && WIFEXITED(status))
        printf("[Parent] Child2 exited with status %d\n", WEXITSTATUS(status));

    printf("[Parent] Now waiting for Child1 (PID=%d)\n", child1);

    /* Wait for child1 */
    result = waitpid(child1, &status, 0);
    if (result == child1 && WIFEXITED(status))
        printf("[Parent] Child1 exited with status %d\n", WEXITSTATUS(status));

    return 0;
}

Example 2: Non-Blocking Poll with WNOHANG

The parent does other work and periodically checks if the child has finished.

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

int main(void)
{
    pid_t child, result;
    int status;
    int poll_count = 0;

    child = fork();
    if (child == 0) {
        printf("[Child] PID=%d, doing work for 3 seconds\n", getpid());
        sleep(3);
        _exit(42);
    }

    /* Parent polls every 1 second while doing its own work */
    while (1) {
        result = waitpid(child, &status, WNOHANG);

        if (result == 0) {
            /* Child not done yet */
            poll_count++;
            printf("[Parent] Poll #%d: child still running, doing other work...\n",
                   poll_count);
            sleep(1); /* simulate doing other work */
        } else if (result == child) {
            /* Child finished */
            printf("[Parent] Child finished after %d polls\n", poll_count);
            if (WIFEXITED(status))
                printf("[Parent] Child exit status: %d\n", WEXITSTATUS(status));
            break;
        } else {
            perror("waitpid");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

WNOHANG is ideal when the parent cannot afford to block — for example in event loops or servers.

Example 3: Detecting Stopped Child with WUNTRACED

The parent detects when the child is stopped by SIGSTOP, not just when it exits.

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

int main(void)
{
    pid_t child, result;
    int status;

    child = fork();
    if (child == 0) {
        /* Child: loop indefinitely, waiting for signals */
        printf("[Child] PID=%d running. Send SIGSTOP to stop me.\n", getpid());
        for (;;)
            pause(); /* wait for any signal */
    }

    printf("[Parent] Waiting for child PID=%d (stop or exit)\n", child);

    /* Use WUNTRACED to also catch stop events */
    for (;;) {
        result = waitpid(child, &status, WUNTRACED | WCONTINUED);
        if (result == -1) {
            perror("waitpid");
            break;
        }

        if (WIFEXITED(status)) {
            printf("[Parent] Child exited, status=%d\n", WEXITSTATUS(status));
            break;
        } else if (WIFSIGNALED(status)) {
            printf("[Parent] Child killed by signal %d\n", WTERMSIG(status));
            break;
        } else if (WIFSTOPPED(status)) {
            printf("[Parent] Child stopped by signal %d\n", WSTOPSIG(status));
            /* Could resume with kill(child, SIGCONT) */
        } else if (WIFCONTINUED(status)) {
            printf("[Parent] Child resumed\n");
        }
    }

    return 0;
}
/*
 * Test:
 *   Run program, note child PID
 *   kill -STOP    -> parent prints "Child stopped"
 *   kill -CONT    -> parent prints "Child resumed"
 *   kill -TERM    -> parent prints "Child killed"
 */

waitpid() vs wait() Equivalence

These two calls are exactly equivalent:

wait(&status);
waitpid(-1, &status, 0);  /* same thing */

waitpid(-1, ...) means “any child”, and 0 options means “block”.

Key Terms:

waitpid() WNOHANG WUNTRACED WCONTINUED SIGSTOP SIGCONT poll non-blocking

Interview Questions

Q1. What is the key difference between wait() and waitpid()?

wait() waits for any child and always blocks. waitpid() can target a specific child, use non-blocking mode (WNOHANG), and detect stopped children (WUNTRACED).

Q2. What does waitpid() return when WNOHANG is used and no child has changed state?

It returns 0 (not -1). A return of -1 with ECHILD means there are no matching children at all.

Q3. What is the difference between WNOHANG returning 0 vs -1?

0 means the child exists but hasn’t changed state yet. -1 (ECHILD) means no matching child process exists.

Q4. How do you wait for any child in the same process group as the caller?

waitpid(0, &status, 0);

Q5. What happens if you pass a PID of a process that is not your child to waitpid()?

It returns -1 with errno = ECHILD, because you can only wait for your own children.

Q6. What flag must you use to detect when a child is resumed from a stop?

WCONTINUED. Available since Linux 2.6.10. Check with WIFCONTINUED(status).

Q7. Can WNOHANG and WUNTRACED be combined?

Yes, using bitwise OR: waitpid(pid, &status, WNOHANG | WUNTRACED).

Q8. In a server that creates many children, why is WNOHANG preferred over blocking wait?

Blocking wait would freeze the server’s main loop. WNOHANG allows the server to poll periodically and continue accepting connections.

Next: Wait Status Macros — Decoding What Happened to Your Child

Part 3: Wait Status → EmbeddedPathashala Home

Leave a Reply

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