How to Use the waitpid Function in C

Jinku Hu Feb 02, 2024
  1. Use the waitpid Function to Monitor Child Process Status in C
  2. Use Macros to Display Wait Status of Child Process in C
How to Use the waitpid Function in C

This article will demonstrate multiple methods about how to use the waitpid function in C.

Use the waitpid Function to Monitor Child Process Status in C

In Unix-based systems, there is a notion of a process that is simply a running instance of a program. The process can create other processes using the fork system call and execute the given portion of the code. Note that, for this topic, system calls are operating system services provided to the user as C-style functions. Generally, in many scenarios, a program is required to monitor processes it created called children. wait function family is the one that provides this functionality, and waitpid is one of them.

The wait system call has multiple limitations, and to cover more advanced features, the waitpid function needs to be utilized. Namely, if a process creates multiple children and the parent needs to monitor a specific child, only waitpid can do this. In the following example, we implement a function named spawnChild that creates a new child process and executes a different program. For the sake of a good demonstration of the example, we are executing a top program (available on almost all Unix-based systems) that is running until the user terminates it. Once the function returns in the parent process, we store a child process ID and pass it to the waitpid function to monitor the status.

The waitpid takes three arguments, the first of which is process id number (pid). PID can have multiple predesignated values with different effects, but in this case, we’re only going to mention -1 and >0. -1 value can be passed to monitor any child process that changes their state first, which is used to implement the wait functionality. >0 implies that the value should be the actual process ID that was returned from the fork function, which in turn is used to monitor only specific child process. The second argument is of type int pointer and we should declare an integer variable to pass its address to the function. waitpid, on the other hand, will store the child status information in the given int variable, which then can be decoded using the predefined macros. The last argument is of type int, and it’s used to specify the certain child process events to monitor in addition to default ones.

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

pid_t spawnChild(const char* program, char** arg_list) {
  pid_t ch_pid = fork();
  if (ch_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (ch_pid > 0) {
    printf("spawn child with pid - %d\n", ch_pid);
    return ch_pid;
  } else {
    execvp(program, arg_list);
    perror("execve");
    exit(EXIT_FAILURE);
  }
}

int main(void) {
  const char* args[] = {"top", NULL, NULL};

  pid_t child;
  int wstatus;

  child = spawnChild("top", args);

  if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
    perror("waitpid");
    exit(EXIT_FAILURE);
  }

  exit(EXIT_SUCCESS);
}

Use Macros to Display Wait Status of Child Process in C

Note that the parent process is suspended when the waitpid function is called and it does not resume the execution until the monitored child process changes state. The next example shows the waitpid call with WUNTRACED and WCONTINUED arguments, which implies that a parent monitors if the child has been stopped or continued by corresponding signals. Also, we implemented the printWaitStatus function that can be called to print the retrieved child status. Notice that it’s using W* type macros defined in <sys/wait.h> header to extract the encoded information from the status integer variable.

There are some conditional preprocessor definitions since not all macros are available on all platforms, thus ensuring that function is portable and compiles successfully regardless.

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

pid_t spawnChild(const char* program, char** arg_list) {
  pid_t ch_pid = fork();
  if (ch_pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (ch_pid > 0) {
    printf("spawn child with pid - %d\n", ch_pid);
    return ch_pid;
  } else {
    execvp(program, arg_list);
    perror("execve");
    exit(EXIT_FAILURE);
  }
}

void printWaitStatus(int status) {
  if (WIFEXITED(status)) {
    printf("child exited, status=%d\n", WEXITSTATUS(status));
  } else if (WIFSIGNALED(status)) {
    printf("child killed by signal %d (%s)", WTERMSIG(status),
           strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP
    if (WCOREDUMP(status)) printf(" (core dumped)");
#endif
    printf("\n");
  } else if (WIFSTOPPED(status)) {
    printf("child stopped by signal %d (%s)\n", WSTOPSIG(status),
           strsignal(WSTOPSIG(status)));
#ifdef WIFCONTINUED
  } else if (WIFCONTINUED(status)) {
    printf("child continued\n");
#endif
  } else {
    printf("status=%x\n", (unsigned int)status);
  }
}

int main(void) {
  const char* args[] = {"top", NULL, NULL};

  pid_t child;
  int wstatus;

  child = spawnChild("top", args);

  if (waitpid(child, &wstatus, WUNTRACED | WCONTINUED) == -1) {
    perror("waitpid");
    exit(EXIT_FAILURE);
  }

  printWaitStatus(wstatus);

  exit(EXIT_SUCCESS);
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C Process