16.19. Avoiding Zombie ProcessesProblemYour program forks children, but the dead children accumulate, fill up your process table, and aggravate your system administrator. SolutionIf you don't need to record the children that have terminated, use: $SIG{CHLD} = 'IGNORE';
To keep better track of deceased children, install a
SIGCHLD handler to call use POSIX ":sys_wait_h"; $SIG{CHLD} = \&REAPER; sub REAPER { my $stiff; while (($stiff = waitpid(-1, &WNOHANG)) > 0) { # do something with $stiff if you want } $SIG{CHLD} = \&REAPER; # install *after* calling waitpid } Discussion
When a process exits, the system keeps it in the process table so the parent can check its status - whether it terminated normally or abnormally. Fetching a child's status (thereby freeing it to drop from the system altogether) is rather grimly called
reaping
dead children. (This entire recipe is full of ways to harvest your dead children. If this makes you queasy, we understand.) It involves a call to
To avoid accumulating dead children, simply tell the system that you're not interested in them by setting
The
The
Because the kernel keeps track of undelivered signals using a bit vector, one bit per signal, if two children die before your process is scheduled, you will get only a single SIGCHLD. You must always loop when you reap in a SIGCHLD handler, and so you can't use
Both $exit_value = $? >> 8; $signal_num = $? & 127; $dumped_core = $? & 128; The standard POSIX module has macros to interrogate status values: WIFEXITED, WEXITSTATUS, WIFSIGNALLED, and WTERMSIG. Oddly enough, POSIX doesn't have a macro to test whether the process core dumped.
Beware of two things when using SIGCHLD. First, the system doesn't just send a SIGCHLD when a child exits; it also sends one when the child stops. A process can stop for many reasons - waiting to be foregrounded so it can do terminal I/O, being sent a SIGSTOP (it will wait for the SIGCONT before continuing), or being suspended from its terminal. You need to check the status with the
use POSIX qw(:signal_h :errno_h :sys_wait_h); $SIG{CHLD} = \&REAPER; sub REAPER { my $pid; $pid = waitpid(-1, &WNOHANG); if ($pid == -1) { # no child waiting. Ignore it. } elsif (WIFEXITED($?)) { print "Process $pid exited.\n"; } else { print "False alarm on $pid.\n"; } $SIG{CHLD} = \&REAPER; # in case of unreliable signals }
The second trap with SIGCHLD is related to Perl, not the operating system. Because
Most systems support a non-blocking use Config; $has_nonblocking = $Config{d_waitpid} eq "define" || $Config{d_wait4} eq "define"; System V defines SIGCLD, which has the same signal number as SIGCHLD but subtly different semantics. Use SIGCHLD to avoid confusion. See Also
The
"Signals"
sections in
Chapter 6
of
Programming Perl
and in
perlipc
(1); the
Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|