in reply to Re: Protection from zombies
in thread Protection from zombies

The new process does remain a child of the parent process across exec, since the open(FROM_CHILD,'-|') does an implicit fork. Here's an example:
#!/usr/bin/perl use warnings; use strict; my $pid = open(F,"-|"); if (!defined($pid)) { die "fork error: $!\n"; } if ($pid) { # Parent print "PARENT: PID is $$\n"; sleep(1000); print <F>; close(F); } else { # Child exec("/bin/bash","-c", 'echo CHILD: PID is $$, PPID is $PPID >&2'); }

Below, notice that 20474 is the parent process, and its child 20476 is a zombie.

freenet{swgsh}~ $ perl tmp/t3 & [2] 20474 freenet{swgsh}~ $ PARENT: PID is 20474 CHILD: PID is 20476, PPID is 20474 freenet{swgsh}~ $ ps PID TTY TIME CMD 12912 pts/0 00:00:00 bash 13286 pts/0 00:00:39 emacs 20474 pts/0 00:00:00 perl 20476 pts/0 00:00:00 bash <defunct> 20487 pts/0 00:00:00 ps

Replies are listed 'Best First'.
Re^3: Protection from zombies
by mda2 (Hermit) on May 16, 2005 at 22:58 UTC
    Change your code, adding this single sequence:
    $SIG{CHLD} = sub { my $pid = waitpid(-1, 0); print ">> eof PID $pid -> $?\n"; };
    The new result is:
    PARENT: PID is 3433 CHILD: PID is 3434, PPID is 3433 >> eof pid 3434 -> 0
    Other samples and techniques at perlipc (find REAPER ;)

    --
    Marco Antonio
    Rio-PM

      Right. Moving the sleep after the close works too, since the close does a waitpid.
      First of all, this seems to not play along very well with my code. For some reason (I do many systems and forks), setting a global SIGCHLD handler makes my program behave very badly in ways I'm not willing to debug. Second, I know I could use the handler locally only in the part I shown in the original post, but I'm wondering why the waitpid done by the close of the pipe does not work. Third, and most interesting, here's a C test I wrote:
      #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> static char buf[512]; int main() { int pfd[2]; pipe( pfd ); pid_t pid = fork(); if ( pid ) { /* parent */ ssize_t s = read( pfd[0], buf, sizeof(buf) ); waitpid( pid, NULL, 0 ); } else { /* child */ dup2( pfd[1], STDOUT_FILENO ); close( STDOUT_FILENO ); } return 0; }
      This is equivalent to the Perl code I wrote in the original post. As strace shows, the parent gets stuck in the read call even if the child closes its stdout. Adding a write of 512 bytes to stdout to the child before it closes it makes the read in the parent return and everything works correctly. But that leaves me with the same question: how can the parent not get stuck in its read if I can't control what the child does?
        I've found how to make my C code right: in the parent and child, I have to close the ends of the pipe that I won't be using. My code becomes:
        #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> static char buf[512]; int main() { int pfd[2]; pipe( pfd ); pid_t pid = fork(); if ( pid ) { /* parent */ close( pfd[1] ); ssize_t s = read( pfd[0], buf, sizeof(buf) ); waitpid( pid, NULL, 0 ); } else { /* child */ close( pfd[0] ); dup2( pfd[1], STDOUT_FILENO ); close( STDOUT_FILENO ); } return 0; }
        This seems to be bulletproof. How can I get that using Perl?