Handling Zombies
Obviously we do not want to leave zombies around. They take up space in the kernel and eventually we can run out of processes. Whenever we fork children, we must wait for them to prevent them from becoming zombies. To do this, we establish a signal handler to catch SIGCHLD, and within the handler, we call wait. (We will describe the wait and waitpid functions in Section 5.10(See 8.3.10).) We establish the signal handler by adding the function call
Signal (SIGCHLD, sig_chld);
Warning: Calling standard I/O functions such as printf in a signal handler is not recommended, for reasons that we will discuss in Section 11.18(See 8.9.18). We call printf here as a diagnostic tool to see when the child terminates.
Under System V and Unix 98, the child of a process does not become a zombie if the process sets the disposition of SIGCHLD to SIG_IGN. Unfortunately, this works only under System V and Unix 98. POSIX explicitly states that this behavior is unspecified. The portable way to handle zombies is to catch SIGCHLD and call wait or waitpid.
The sequence of steps is as follows:
1.We terminate the client by typing our EOF character. The client TCP sends a FIN to the server and the server responds with an ACK.
2.The receipt of the FIN delivers an EOF to the child’s pending readline. The child terminates.
3.The parent is blocked in its call to accept when the SIGCHLD signal is delivered. The sig_chld function executes (our signal handler), wait fetches the child’s PID and termination status, and printf is called from the signal handler. The signal handler returns.
4.Since the signal was caught by the parent while the parent was blocked in a slow system call (accept), the kernel causes the accept to return an error of EINTR (interrupted system call). The parent does not handle this error (Figure 5.2(See 8.3.2)), so it aborts.
‘wait’ and ‘waitpid’ Functions
In Figure 5.7(See 8.3.9), we called the wait function to handle the terminated child.
#include <sys/wait.h>
pid_t wait (int *statloc);
pid_t waitpid (pid_t pid, int *statloc, int options);
#include <sys/wait.h>
wait and waitpid both return two values: the return value of the function is the process ID of the terminated child, and the termination status of the child (an integer) is returned through the statloc pointer. There are three macros that we can call that examine the termination status and tell us if the child terminated normally, was killed by a signal, or was just stopped by job control. Additional macros let us then fetch the exit status of the child, or the value of the signal that killed the child, or the value of the job-control signal that stopped the child. We will use the WIFEXITED and WEXITSTATUS macros in Figure 15.10(See 9.4.7) for this purpose.
If there are no terminated children for the process calling wait, but the process has one or more children that are still executing, then wait blocks until the first of the existing children terminates.
waitpid gives us more control over which process to wait for and whether or not to block. First, the pid argument lets us specify the process ID that we want to wait for. A value of -1 says to wait for the first of our children to terminate. (There are other options, dealing with process group IDs, but we do not need them in this text.) The options argument lets us specify additional options. The most common option is WNOHANG. This option tells the kernel not to block if there are no terminated children.
Difference between wait and waitpid
We now illustrate the difference between the wait and waitpid functions when used to clean up terminated children. To do this, we modify our TCP client as shown in Figure 5.9. The client establishes five connections with the server and then uses only the first one (sockfd[0]) in the call to str_cli. The purpose of establishing multiple connections is to spawn multiple children from the concurrent server, as shown in Figure 5.8.
本文详细讲解了如何在程序中使用wait和waitpid函数处理子进程的终止,包括设置SIGCHLD信号处理、诊断工具的使用,以及waitpid的更细粒度控制。通过实例演示了wait和waitpid在多连接TCP客户端中的应用区别。
1484

被折叠的 条评论
为什么被折叠?



