目次
- 1 1. Introduction
- 2 2. What is the C language wait function?
- 3 3. Basic usage of the wait function
- 4 4. Relationship between Zombie Processes and wait Function
- 5 5. Differences from the waitpid Function
- 6 6. Synchronization in a Multithreaded Environment
- 6.1 Differences Between Process Synchronization and Thread Synchronization
- 6.2 Synchronization in a Multithreaded Environment
- 6.3 Basics of Synchronization Using Condition Variables
- 6.4 Example of Synchronization Using Condition Variables
- 6.5 Explanation of Sample Code
- 6.6 Points to Consider in Thread Synchronization
- 6.7 Caution When Using the wait Function in a Multithreaded Environment
- 7 7. Troubleshooting and Best Practices
- 8 8. Frequently Asked Questions (FAQ)
- 8.1 Q1: How to prevent zombie processes without using the wait function?
- 8.2 Q2: In what situations should you use waitpid?
- 8.3 Q3: What are the reasons why the wait function does not return a value?
- 8.4 Q4: How to safely use the wait function in multithreaded programs?
- 8.5 Q5: Are there alternatives to the wait function?
- 8.6 Q6: What should you do if a child process does not terminate?
- 8.7 Q7: What is the meaning of the exit status obtained by the wait function?
- 8.8 Q8: How to manage multiple child processes simultaneously?
- 9 9. Summary
1. Introduction
C is widely used for developing system programs and embedded systems, and process management is one of the important topics among them. This article explains thewait
function in C. The wait
function is a system call used to achieve synchronization between processes, especially useful for waiting for child processes to terminate. Through this article, you can learn broadly about the wait
function from basic usage to advanced techniques, and related topics (e.g., the waitpid
function and zombie process mitigation).2. What is the C language wait
function?
wait
function overview
wait
function is one of the system calls used on UNIX-like systems, employed by a parent process to wait for a child process to exit. This enables the parent process to obtain the child’s exit status.Basic operation
wait
function is used as follows.#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent process
wait(NULL); // Wait until the child process terminates
} else if (pid == 0) {
// Child process
printf("Hello from child process!
");
}
return 0;
}
In the above code, a child process is created with the fork
function, and the parent process uses the wait
function to wait for the child’s termination.Return value and arguments
- Return value If the child process has terminated, it returns its process ID. If an error occurs, it returns
-1
. - Argument By passing
int* status
as an argument, you can obtain the child process’s exit status.
3. Basic usage of the wait
function
Simple code example
wait
function basic usage is shown below.#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
int status;
wait(&status); // Retrieve child process exit status
if (WIFEXITED(status)) {
printf("Child exited with status: %d
", WEXITSTATUS(status));
}
} else if (pid == 0) {
printf("Child process running...
");
_exit(0); // Child process termination
}
return 0;
}
Explanation of the sample code
- Waits for the child process to terminate using
wait(&status)
. - Uses
WIFEXITED(status)
to check whether the child process exited normally. - Retrieves the child process’s exit code with
WEXITSTATUS(status)
.
wait
function, the parent process can accurately determine the child process’s termination status.4. Relationship between Zombie Processes and wait
Function
What Is a Zombie Process
A zombie process occurs when a child process has terminated but the parent process fails to properly reap its exit status. In this state, the child process itself has ended, but its process information remains in the system. If many zombie processes accumulate in the system, they can fill up the process table and prevent other processes from running correctly.Example of Zombie Process Creation
Below is a simple example that creates a zombie process.#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent process does nothing and waits
sleep(10);
} else if (pid == 0) {
// Child process
printf("Child process exiting...
");
_exit(0);
}
return 0;
}
In this example, because the parent does not reap the child’s exit status, the child becomes a zombie after it exits.wait
Function Prevents Zombie Processes
wait
function can be used so that the parent properly reaps the child’s exit status and prevents zombie processes. Below is a corrected version.#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent process
wait(NULL); // Prevent zombie processes by waiting for the child to exit
printf("Parent process: Child process has been reaped.
");
} else if (pid == 0) {
// Child process
printf("Child process exiting...
");
_exit(0);
}
return 0;
}
In this program, using wait(NULL)
makes the parent wait for the child’s termination, preventing zombie processes.Key Points for Handling Zombie Processes
- Always use
wait
orwaitpid
Properly reaping a child’s exit status prevents zombie processes. - Using a signal handler You can catch the
SIGCHLD
signal and automatically reap child processes when they exit.
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sigchld_handler(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0); // Reap all terminated child processes
}
int main() {
signal(SIGCHLD, sigchld_handler); // Set SIGCHLD handler
pid_t pid = fork();
if (pid == 0) {
// Child process
printf("Child process exiting...
");
_exit(0);
} else if (pid > 0) {
// Parent process
printf("Parent process doing other work...
");
sleep(10); // SIGCHLD handler may fire while doing other work
}
return 0;
}
With this approach, even if the parent never explicitly calls wait
, terminated child processes are automatically reaped.5. Differences from the waitpid
Function
What is the waitpid
function?
waitpid
is a system call that, like the wait
function, waits for a child process to exit, but it enables more flexible process management. By using waitpid
, you can specify a particular child process or wait in non‑blocking mode.Basic Usage and Syntax
waitpid
function syntax is as follows.#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
pid_t waitpid(pid_t pid, int *status, int options);
pid
Specifies the ID of the child process to wait for. You can control its behavior using the following special values.pid > 0
: Wait for the specified process ID.pid == 0
: Wait for any child process in the same process group as the parent.pid < -1
: Wait for all processes in the specified process group.pid == -1
: Wait for any child process (same aswait
function).status
Pointer that stores the child process’s exit status.options
Specifies options that modify the behavior. The main values are:WNOHANG
: Non-blocking mode. Returns immediately if no child has exited.WUNTRACED
: Also includes child processes that are stopped.- Return value
- On success: PID of the terminated child process.
- If no child process exists:
0
(whenWNOHANG
is specified). - On error:
-1
.
waitpid
Function Example
The following is an example that uses the waitpid
function to wait for a specific child process.#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid1 = fork();
if (pid1 == 0) {
// Child process 1
printf("Child 1 running...
");
sleep(2);
_exit(1);
}
pid_t pid2 = fork();
if (pid2 == 0) {
// Child process 2
printf("Child 2 running...
");
sleep(4);
_exit(2);
}
int status;
// Wait for specific child process pid1
pid_t ret = waitpid(pid1, &status, 0);
if (ret > 0 && WIFEXITED(status)) {
printf("Child 1 exited with status: %d
", WEXITSTATUS(status));
}
// Wait for the remaining child process
waitpid(pid2, &status, 0);
printf("Child 2 exited with status: %d
", WEXITSTATUS(status));
return 0;
}
Main Differences Between wait
and waitpid
Item | wait Function | waitpid Function |
---|---|---|
Target to wait for | Any child process | Can specify a particular child process |
Blocking | Always blocking | Non‑blocking possible |
Option specification | Not available | WNOHANG , WUNTRACED , etc. |
Flexibility | Limited | High |
When to Choose waitpid
- When you need to manage specific child processes It is suitable when a program creates multiple child processes and you want to control each one individually.
- When you want to perform asynchronous processing The
WNOHANG
option is useful when you need to check a process’s exit status without blocking other operations.
Selection Guide for wait
and waitpid
- Simple programs can use the
wait
function. If you only need to wait for any child process, flexibility is not required. - Complex process management should use the
waitpid
function. Especially when asynchronous processing or control of specific processes is needed, usingwaitpid
allows efficient management.
6. Synchronization in a Multithreaded Environment
Differences Between Process Synchronization and Thread Synchronization
C language treats processes and threads as distinct management units. Process synchronization (e.g.,wait
and waitpid
functions) controls termination states and resource sharing among multiple processes. In contrast, thread synchronization manages resources and ordering between threads within the same process.Synchronization in a Multithreaded Environment
When synchronizing between threads, condition variables and mutexes are commonly used. Here we explain how to synchronize using condition variables from thepthread
library.Basics of Synchronization Using Condition Variables
By using condition variables (pthread_cond_t
), you can efficiently handle waiting and notification between threads.Basic Functions of Condition Variables
pthread_cond_wait
Waits until the condition is satisfied. The mutex is released while waiting.pthread_cond_signal
Wakes up one waiting thread.pthread_cond_broadcast
Wakes up all waiting threads.
Example of Synchronization Using Condition Variables
Below is a simple example that synchronizes multiple threads using a condition variable.#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// Initialize mutex and condition variable
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int shared_data = 0;
void* producer(void* arg) {
pthread_mutex_lock(&mutex);
printf("Producer: producing data...
");
shared_data = 1;
// Notify via condition variable
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
void* consumer(void* arg) {
pthread_mutex_lock(&mutex);
// Wait until the condition is satisfied
while (shared_data == 0) {
printf("Consumer: waiting for data...
");
pthread_cond_wait(&cond, &mutex);
}
printf("Consumer: consumed data!
");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t producer_thread, consumer_thread;
// Create threads
pthread_create(&consumer_thread, NULL, consumer, NULL);
sleep(1); // Sleep so that the consumer waits first
pthread_create(&producer_thread, NULL, producer, NULL);
// Wait for thread termination
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
return 0;
}
Explanation of Sample Code
- Use of Mutex
pthread_mutex_lock
andpthread_mutex_unlock
are used to control exclusive access to shared resources. - Waiting and Notification with Condition Variables
- The consumer thread waits for
shared_data
to be updated usingpthread_cond_wait
. - The producer thread notifies the consumer thread with
pthread_cond_signal
.
- Cooperative Operation Between Threads The producer generates data and the consumer receives it, implementing a simple flow.
Points to Consider in Thread Synchronization
- Preventing Deadlocks You need to be careful about the order of locking and unlocking mutexes.
- Avoiding Race Conditions When threads attempt to modify a condition simultaneously, you must correctly combine condition variables with mutexes.
- Considering Scalability To synchronize efficiently among multiple threads, it is important to minimize unnecessary waiting and locking.
Caution When Using the wait
Function in a Multithreaded Environment
The wait
function is intended for synchronization between processes and is not suitable for thread-level synchronization. For synchronizing threads, using condition variables or mutexes is safer and more efficient.
7. Troubleshooting and Best Practices
Common Errors and Solutions
When usingwait
or waitpid
in C, several typical errors can occur. This section explains their causes and solutions.1. wait
function returns an error
Cause- The child process does not exist.
- The system call was interrupted (
EINTR
error).
- Check whether the child process exists.
- If the system call is interrupted, retry in a loop.
int status;
while (wait(&status) == -1) {
if (errno != EINTR) {
perror("wait failed");
break;
}
}
2. Zombie processes occur
Cause- The parent process does not reap the child process’s termination.
- Use
wait
orwaitpid
properly in the parent process. - Set up a signal handler to automatically reap terminations.
3. Unstable behavior due to race conditions
Cause- Multiple parent processes attempt to wait for the same child process.
- The child’s termination status is not correctly reaped.
- If you need to specify a process ID explicitly, use
waitpid
. - Design the system so that multiple parent processes do not manage the same child process.
Best Practices
1. Proper selection of wait
and waitpid
- For simple programs,
wait
is sufficient. - When complex process management (controlling specific child processes or asynchronous handling) is needed, use
waitpid
.
2. Leveraging signal handlers
Using a signal handler allows the parent process to automatically reap a child’s termination status without explicitly callingwait
. This prevents zombie processes while keeping the parent’s code concise.3. Thorough error handling
wait
and waitpid
are system calls, so errors can occur. Check the return value of every call and handle errors appropriately.pid_t pid = wait(NULL);
if (pid == -1) {
perror("wait error");
}
4. Implementing asynchronous processing
When implementing asynchronous processing, the following design is recommended.- Do not block the main processing; periodically call
waitpid
withWNOHANG
. - Poll the child’s termination status and reap it as needed.
int status;
pid_t pid;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("Child process %d terminated.
", pid);
}
5. Managing the number of processes
Programs that spawn many child processes need to control the number of processes.- Limit the number of child processes created simultaneously.
- Adopt a design that does not spawn new processes until existing child processes have terminated.
8. Frequently Asked Questions (FAQ)
Q1: How to prevent zombie processes without using the wait
function?
A: wait
functionを使用しなくても、シグナルハンドラーを活用することでゾンビプロセスを防ぐことができます。親プロセスがSIGCHLD
シグナルを捕捉することで、子プロセスの終了状態を自動的に回収します。 以下はシグナルハンドラーを使った例です。#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handle_sigchld(int sig) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int main() {
signal(SIGCHLD, handle_sigchld);
if (fork() == 0) {
// child process
_exit(0);
}
// parent process work
sleep(5);
printf("Parent process completed.\n");
return 0;
}
Q2: In what situations should you use waitpid
?
A: waitpid
は以下のような場合に適しています。- When you want to wait for a specific child process only (specify
pid
). - When you want to manage processes asynchronously (use the
WNOHANG
option). - When you want to check stopped child processes (use the
WUNTRACED
option).
Q3: What are the reasons why the wait
function does not return a value?
A: The following reasons can cause the wait
function to not return a value (return -1
).- 子プロセスが存在しない。
- The process may have already terminated, or
fork
may have failed.
- システムコールが中断された(
EINTR
エラー)。
- If the interruption is caused by a signal, retry in a loop.
int status;
pid_t pid;
while ((pid = wait(&status)) == -1) {
if (errno != EINTR) {
perror("wait error");
break;
}
}
Q4: How to safely use the wait
function in multithreaded programs?
A: When using wait
in multithreaded programs, keep the following points in mind.wait
operates at the process level, so use condition variables or mutexes for thread-level synchronization.- Managing child processes is generally safest when limited to the main thread. If other threads call
wait
, it may cause unexpected behavior.
Q5: Are there alternatives to the wait
function?
A: The following alternatives are available.waitpid
function offers high flexibility and can control specific processes.- Signal handler processes child termination asynchronously.
- Event-driven programming can also manage process termination using an event loop (e.g.,
select
orpoll
).
Q6: What should you do if a child process does not terminate?
A: The child process may be stopped. In this case, follow these steps.- Use the
WUNTRACED
option to check for stopped child processes. - If needed, terminate the child process with the
kill
system call.
Q7: What is the meaning of the exit status obtained by the wait
function?
A: The status
obtained by the wait
function contains the following information.- Normal exit:
WIFEXITED(status)
returns true, and the exit code can be retrieved withWEXITSTATUS(status)
. - Abnormal termination: If terminated by a signal,
WIFSIGNALED(status)
returns true, and the terminating signal can be obtained withWTERMSIG(status)
.
int status;
wait(&status);
if (WIFEXITED(status)) {
printf("Exited with code: %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("Killed by signal: %d\n", WTERMSIG(status));
}
Q8: How to manage multiple child processes simultaneously?
A: When managing multiple child processes, a common approach is to usewaitpid
in a loop to reap all processes.pid_t pid;
int status;
while ((pid = waitpid(-1, &status, 0)) > 0) {
printf("Child process %d exited.\n", pid);
}
9. Summary
In this article, we provided a comprehensive overview of the C languagewait
function, covering everything from basic usage to advanced examples and related topics (such as the waitpid
function and zombie process mitigation). Below are the key points summarized.Fundamentals of the wait
Function
wait
is a system call that allows a parent process to wait for a child process to terminate and to reap its termination status.- Using
wait(NULL)
lets you wait for a child process without checking its exit status.
Advanced Usage and Related Topics
- Zombie Process Mitigation If a child’s termination status is not reaped, a zombie process will appear. Proper use of
wait
andwaitpid
prevents this. waitpid
Function It is very useful when you need to wait for a specific child process or perform asynchronous handling.- Signal Handler By using the
SIGCHLD
signal, a parent can automatically reap termination statuses without explicitly callingwait
.
Considerations in Multithreaded Environments
- The
wait
function synchronizes between processes, but for thread-level synchronization you should use condition variables or mutexes. - It is important to design your program so that multiple threads do not call
wait
simultaneously.
Troubleshooting and Best Practices
- Always check the return values of
wait
andwaitpid
and handle errors appropriately. - When asynchronous handling is needed, using the
WNOHANG
option enables efficient process management. - Aim for a simple design, avoiding unnecessary process creation and complex dependencies.
What to Learn Next
Having mastered the basics of thewait
function and process synchronization, the next step is to study the following topics:- Interprocess Communication (IPC) Methods for exchanging data using pipes, message queues, and shared memory.
- Asynchronous Programming How to leverage event-driven programming and asynchronous I/O.
fork
Function and Process Creation Details Memory management and behavior of process branching when creating child processes.
wait
function and its related concepts. By implementing proper process management, you can develop efficient and stable programs.