in reply to Protection from zombies

Update:What I wrote originally (below) is quite incorrect; the child process morphs into the exec'd process, retaining its PID and PPID. I had misconstrued some statements about exec in IPC::Open3. Many thanks++ to sgifford for the correction.


Why do you say the "child has become a zombie"? You've exec'd that process; it is not a child of the parent anymore. The actual child presumably dies peacefully after the exec (assuming the call to exec succeeds, which says nothing about the fate of the exec'd process) and gets reaped automatically when FROM_CHILD is closed.

If by "becoming a zombie" you mean that the exec'd process terminates abnormally, then all you need to do to avoid the idling is to time out the parent.

the lowliest monk

Replies are listed 'Best First'.
Re^2: Protection from zombies
by sgifford (Prior) on May 16, 2005 at 21:58 UTC
    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:

    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
      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?