How to Use the waitpid Function in C
-
Use the
waitpid
Function to Monitor Child Process Status in C - Use Macros to Display Wait Status of Child Process 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);
}
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