In C, we can use various system calls available through multiple libraries. A system call is a low-level mechanism allowing users to communicate with the operating system kernel for multiple functions.
In this Answer, we will discuss two system calls wait()
and waitpid()
that are provided by the unistd.h
library and are used to wait for processes to complete their executions.
Note: Learn more about the
wait()
andwaitpid()
functions in the Answers below:
wait()
vs waitpid()
Both these system calls are used in processes that have a parent-child relationship to make the parent processes wait for a child process. These system calls return the process id of the child process for which the parent had waited upon execution completion and can be used to obtain information about the child's exit status.
Let's look at an illustration to understand how they work.
The diagram shows two processes, parent and child, currently being executed. The parent process calls wait()
or waitpid()
. It gets blocked and waits for the child to finish its execution before resuming. The child, when exits and terminates, the parent gets unblocked and continues its execution from where it left off.
In the illustration, we say that using wait()
or waitpid()
yields the same result, so why are there two different functions? Let’s look into this.
wait()
and waitpid()
different?The wait()
function is a simple system call used by parent processes to wait for any arbitrary child to finish its execution. Till then, the parent process becomes blocked. If the parent process has numerous children, it can't wait for a specific child using the wait()
function. So how do we make it possible for the parent process to wait for a specific child to finish its execution before continuing?
For this purpose, we have the waitpid()
function that is used when the parent process wants to wait for a specific child. In the function, we pass the process id of the child process for which we want to wait, and the parent becomes blocked till that specific child terminates.
Now that we have observed what is the difference between wait()
and waitpid()
, let's look at the C application below that illustrates this difference.
In the application, we have created four processes, 3 children and 1 parent, using two fork()
system calls. We then use the wait()
or waitpid()
function in the parent process to make the parent wait for a child to exit before continuing its execution.
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> int main(){ int cpid = fork(); int cpid2 = fork(); if(cpid != 0 && cpid2 != 0){ int status; printf("\nParent: I am going to wait for process with ID: %d\n", cpid2); // wait for any child to exit int waitPID = wait(&status); // wait for cpid2 to exit //int waitPID = waitpid(cpid2, &status, 0); printf("\nParent: Waited for child, return value of waitpid(): %d\n", waitPID); printf("\nParent: Exit code of terminated child: %d\n", WEXITSTATUS(status)); exit(1); } else if(cpid == 0 && cpid2 != 0){ printf("\nChild1: My process ID: %d, my exit code is 1\n", getpid()); sleep(1); exit(1); } else if(cpid != 0 && cpid2 == 0){ printf("\nChild2: My process ID: %d, my exit code is 2\n", getpid()); sleep(1); exit(2); } else{ printf("\nChild3: My process ID: %d, my exit code is 3\n", getpid()); sleep(1); exit(3); } return 0; }
wait()
functionIf we use the wait()
function, each time we run the program, the parent process may wait for a different child process instead of the same one. This can be observed by examining the return value of the wait()
function in the parent's print statements.
Hence, we can't use the wait()
function to make the parent wait for a specific process to terminate.
waitpid()
functionNow, if we use the waitpid()
function in the above code, we see that the waitpid()
function will always wait for the specified child with the process id that is stored in the variable cpid2
even if we keep re-running the program.
We can verify this by comparing the process id of the process named Child2
with the print statement of the parent that prints the return value of the waitpid()
function.
Note: To use the
waitpid()
function, comment out the line numbered 15 and un-comment the line numbered 18.
Lines 7–8: We use two fork()
system calls to create 3 children and 1 parent process.
Lines 10–23: Here, we write code for the parent process in which we can use the wait()
and the waitpid()
functions.
Line 15: Here, we see the wait()
function that is used to wait for any child to exit, we pass it a status variable to retrieve the exit status of the child process.
Line 18: This is the waitpid()
function in which we pass the child's process id to wait (cpid2)
and the status variable. We also pass a 0
as the third argument because we want it to run as its default behavior without any flags.
Lines 20–21: We print the return value of the wait()
or waitpid()
functions along with the value in the status variable using a pre-defined function-like macro WEXITSTATUS()
that extracts the exit status of the child process from the integer variable named status
.
Lines 24–38: Here, we can see the codes for the children processes. Each child process prints its process id and a different exit value corresponding to its name.
The wait()
function is a generalized form of the waitpid()
function that is used when the parent wants to wait for any child to exit. It cannot be used to wait for a specific child before continuing its execution. For this purpose, we use the waitpid()
function to specify exactly what child process the parent should wait for.