The lifetimes of parent and child processes are usually not the same—either the parent outlives the child or vice versa. This raises two questions:
Who becomes the parent of an orphaned child? The orphaned child is adopted by init, the ancestor of all processes, whose process ID is 1. In other words, after a child’s parent terminates, a call to getppid() will return the value 1. This can be used as a way of determining if a child’s true parent is still alive (this assumes a child that was created by a process other than init).
Using the PR_SET_PDEATHSIG
operation of the Linux-specific prctl() system call, it is possible to arrange that a process receives a specified signal when it becomes an orphan.
What happens to a child that terminates before its parent has had a chance to perform a wait()? The point here is that, although the child has finished its work, the parent should still be permitted to perform a wait() at some later time to determine how the child terminated. The kernel deals with this situation by turning the child into a zombie. This means that most of the resources held by the child are released back to the system to be reused by other processes. The only part of the process that remains is an entry in the kernel’s process table recording (among other things) the child’s process ID, termination status, and resource usage statistics (Process Resource Usage).
Regarding zombies, UNIX systems imitate the movies—a zombie process can’t be killed by a signal, not even the (silver bullet) SIGKILL
. This ensures that the parent can always eventually perform a wait().
When the parent does perform a wait(), the kernel removes the zombie, since the last remaining information about the child is no longer required. On the other hand, if the parent terminates without doing a wait(), then the init process adopts the child and automatically performs a wait(), thus removing the zombie process from the system.
If a parent creates a child, but fails to perform a wait(), then an entry for the zombie child will be maintained indefinitely in the kernel’s process table. If a large number of such zombie children are created, they will eventually fill the kernel process table, preventing the creation of new processes. Since the zombies can’t be killed by a signal, the only way to remove them from the system is to kill their parent (or wait for it to exit), at which time the zombies are adopted and waited on by init, and consequently removed from the system.
These semantics have important implications for the design of long-lived parent processes, such as network servers and shells, that create numerous children. To put things another way, in such applications, a parent process should perform wait() calls in order to ensure that dead children are always removed from the system, rather than becoming long-lived zombies. The parent may perform such wait() calls either synchronously, or asynchronously, in response to delivery of the SIGCHLD
signal, as described in Establishing a Handler for SIGCHLD.
Example 26-4 demonstrates the creation of a zombie and that a zombie can’t be killed by SIGKILL
. When we run this program, we see the following output:
$./make_zombie
Parent PID=1013 Child (PID=1014) exiting 1013 pts/4 00:00:00 make_zombie Output fromps(1)
1014 pts/4 00:00:00 make_zombie <defunct> After sending SIGKILL to make_zombie (PID=1014): 1013 pts/4 00:00:00 make_zombie Output fromps(1)
1014 pts/4 00:00:00 make_zombie <defunct>
In the above output, we see that ps(1) displays the string <defunct>
to indicate a process in the zombie state.
The program in Example 26-4 uses the system() function to execute the shell command given in its character-string argument. We describe system() in detail in Section 27.6.
Example 26-4. Creating a zombie child process
procexec/make_zombie.c
#include <signal.h> #include <libgen.h> /* For basename() declaration */ #include "tlpi_hdr.h" #define CMD_SIZE 200 int main(int argc, char *argv[]) { char cmd[CMD_SIZE]; pid_t childPid; setbuf(stdout, NULL); /* Disable buffering of stdout */ printf("Parent PID=%ld\n", (long) getpid()); switch (childPid = fork()) { case -1: errExit("fork"); case 0: /* Child: immediately exits to become zombie */ printf("Child (PID=%ld) exiting\n", (long) getpid()); _exit(EXIT_SUCCESS); default: /* Parent */ sleep(3); /* Give child a chance to start and exit */ snprintf(cmd, CMD_SIZE, "ps | grep %s", basename(argv[0])); cmd[CMD_SIZE - 1] = '\0'; /* Ensure string is null-terminated */ system(cmd); /* View zombie child */ /* Now send the "sure kill" signal to the zombie */ if (kill(childPid, SIGKILL) == -1) errMsg("kill"); sleep(3); /* Give child a chance to react to signal */ printf("After sending SIGKILL to zombie (PID=%ld):\n", (long) childPid); system(cmd); /* View zombie child again */ exit(EXIT_SUCCESS); } }procexec/make_zombie.c