templedf has asked for the wisdom of the Perl Monks concerning the following question:

I would really appreciate if someone could explain this one to me. My code is:

#!/usr/bin/perl sub nanny { print "ENTER\n"; while ((my $cpid = waitpid(-1, WNOHANG)) > 0) { print "$cpid exited\n"; } $SIG{CHLD} = \&nanny; print "EXIT\n"; } $SIG{CHLD} = \&nanny; my $c1 = open (C1, "-|"); if ($c1 == 0) { exec("sleep 2"); die $!; } elsif (!defined $c1) { die "Failed: $!\n"; } my $c2 = open (C2, "-|"); if ($c2 == 0) { exec("sleep 7"); die $!; } elsif (!defined $c2) { die "Failed: $!\n"; } my $c3 = open (C3, "-|"); if ($c3 == 0) { exec("sleep 10"); die $!; } elsif (!defined $c3) { die "Failed: $!\n"; } sleep 20; print "DONE\n";

When executed, I see:

ENTER 13173 exited 13174 exited 13175 exited EXIT DONE ENTER EXIT

This is Perl 5.16 on Linux.

Why is the signal handler not exiting after reaping the first (and only at the time) zombie child? If I replace the open with an explicit fork(), I get the same results. If I change the fork/exec to backticks, the handler gets called 3 times as expected, and each call fails to find any zombies, also as expected.

Incidentally, the script terminates after the third child exits without waiting for the sleep to complete. Same with fork(). Backticks let the sleep finish.

Thanks!

Replies are listed 'Best First'.
Re: waitpid(-1, WNOHANG) is hanging
by ikegami (Patriarch) on Aug 11, 2015 at 21:03 UTC

    Incidentally, the script terminates after the third child exits without waiting for the sleep to complete.

    If you have a signal handler set up, sleep (and many other system calls) will get interrupted by signals (returning error EINTR). This is a good thing, as it allows your signal handler to be called.

    sub uninterruptible_sleep { my ($dur) = @_; my $until = time() + $dur; while (1) { $dur = $until - time(); last if $dur <= 0; sleep($dur); } return 1; }
Re: waitpid(-1, WNOHANG) is hanging
by ikegami (Patriarch) on Aug 11, 2015 at 20:51 UTC
    Always use use strict; use warnings;. It catches your error.

      Bingo. I was missing the 'use POSIX ":sys_wait_h";'

Re: waitpid(-1, WNOHANG) is hanging
by runrig (Abbot) on Aug 11, 2015 at 20:50 UTC
    Everything happens so quickly, you spawn all 3 processes by the time you are calling the handler to reap the first one, so the handler reaps all 3. Or close enough. It seems one of the later processes also triggers the handler, but it's already been reaped from the first call. All wrong nevermind.