http://www.perlmonks.org?node_id=927075

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

I'm pretty much a newbie to Perl and am trying to write a script that forks. The child will use Net::RawIP and opens a packet capture descriptor and reads an unspecified number of packets. After 10 seconds, the parent, signals the child to terminate using a KILL signal. The problem is that a <defunct> process for the child remains, at least as far as ps -eaf is concerned. After one minute, the script restarts again. I need to be able to kill the child process after a specified length of time. Any suggestions on the best way to do this?

#!/usr/bin/perl use warnings; use strict; use Net::RawIP; use POSIX; daemonize(); while (1) { my $pid = fork(); print "Process number of script is $$\n"; unless (defined ($pid)) { die "Resources unavailable to fork\n"; } elsif ($pid == 0) { my $a = new Net::RawIP; my $p = $a->pcapinit("eth0","udp port 68",1500,30); my $f = ["User-defined message"]; loop $p,10,\&process_pkt,$f; } elsif ($pid) { for (my $i=0; $i <= 10; $i++ ) { sleep 1; print "in parent...$i seconds have elapsed...\n"; } print "killing child process $pid"; kill 9, $pid; sleep 60; } } # end of while loop sub process_pkt { print "packet captured\n"; my $raw_pkt = $_[2]; my $raw_data = unpack "H*", $raw_pkt; print $raw_data,"\n"; } sub daemonize { my $pid = fork(); exit if $pid; setsid(); }

Replies are listed 'Best First'.
Re: Best way to kill a child process
by jwkrahn (Abbot) on Sep 21, 2011 at 05:44 UTC
Re: Best way to kill a child process
by zentara (Archbishop) on Sep 21, 2011 at 10:26 UTC
    You are probably running into the common problem where you are actually getting the $pid of the shell, from fork(), which is actually running your desired forked program. You need to kill the shell and all it's descendants. Try:
    use Proc::Killfam; killfam 9, $pid;

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Best way to kill a child process
by MidLifeXis (Monsignor) on Sep 21, 2011 at 13:08 UTC

    See previous nodes (press Search after it loads) on the subject as well. In Unix they are called zombie processes.

    What you are seeing is that a data structure containing exit information from the process that died is waiting to be read by the parent process. The spot that the OS stores this information is in the process table (well, close enough for this discussion). You need to use wait or waitpid to capture the information. These functions tell the OS that it can now release the information and clear that slot in the process table.

    --MidLifeXis

Re: Best way to kill a child process
by Marshall (Canon) on Sep 21, 2011 at 13:32 UTC
    When a child dies, the O/S sends a CHLD signal to the parent to let it know that one of its kids has died. There will be an entry in the process table with the exit status of the child or children. This entry in the process table is called a "zombie". This entry takes up system resources and should be removed by the parent. You get rid of the entry by reading the status - most of the time nobody cares what this status is and it is thrown away. This is called "reaping" the child.

    Anyway, the following line of code installs a signal handler for the CHLD signal. When that signal happens, the while loop runs which will read and discard the status of any children who have died (in general case might be more than one). It is ok to put this at the beginning of the code (which means that the child will get one of these too) - but it won't be getting CHLD signals itself.

    $SIG{CHLD} = sub {while (waitpid(-1, WNOHANG) > 0){} };
      $SIG{CHLD} = sub {while (waitpid(-1, WNOHANG) > 0){} };

      AFAIK, on most platforms (where reaping children is of concern), setting

      $SIG{CHLD} = 'IGNORE';

      should have the same effect, as it will make Perl autoreap terminated child processes.  And it's less clutter (you don't need to load/import WNOHANG from POSIX ...).

      See also perlipc.

        Good point about POSIX. My Perl servers usually have:
        use IO::Socket; use POSIX ":sys_wait_h";
        Yes, on some platforms, setting up the sigaction stuff to a NULL handler will cause an "auto reap", but AFAIK that is not universal - I'm thinking about the low level C calls that Perl would use. In this case, this is an issue of how the OS deals with sigaction() handlers, not how Perl itself works. Perl cannot do what C cannot do.

        I guess where I'm at is that the code I suggested is going to work on all platforms all the time (AFIK). I agree that 'IGNORE' will work on almost all platforms. I'm just not sure about the difference between "almost all" and "all". This detail probably doesn't matter for this app - it doesn't sound like a "general purpose" application as far as the OP is concerned.

        So YMMV. Setting "IGNORE" is not "wrong" and it is "easier".

        We both agree on the main issue here:
        that the right way to deal with this is to explicitly do something with the CHLD signal: either a) explicitly ignore it which hopefully will cause the OS to "autoreap" the child or b)set a simple subroutine like I suggested. As long as one of these options "works", then it will work in all cases of child death: a) if the child kills itself (maybe a via a die statement) or b)I kill my own child or c)somebody else kills it.

Re: Best way to kill a child process
by doylebobs (Novice) on Sep 22, 2011 at 06:00 UTC

    Thanks to all for your responses. At the moment, I have decided to set $SIG{CHLD} = 'IGNORE' but will look at the other recommendations in more detail later.