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


in reply to Re: Monitoring Child Process
in thread Monitoring Child Process

Thank you for your suggestion. I incorporated your suggestion by commenting this out... so that there is only one waitpid in the whole script.
foreach (@all_kids) { waitpid ($_, 0); }
And using the REAPER subroutine that you gave. But now two things came up. Under normal condition (without pressing ctrl-c), the parent script exits without 'waiting for the children to finish'.

And when I try to put a 'sleep 20' and the end of the main script, this is the output that I am getting.
Process 1 exited. Process 1 exited.

Replies are listed 'Best First'.
Re^3: Monitoring Child Process
by Marshall (Canon) on Oct 19, 2011 at 20:08 UTC
    Oh, I guess a mis-communication. I updated my post with a more detailed suggestion. This foreach(@all_kids) code is that part should be deleted!

    The REAPER is part of the parent. So having waitpid() in only one place means having it only in REAPER().

    Some replacement code to this foreach (@all_kids) loop:

    while (@dead_kids <10) { sleep(1); } print "all kids dead .. @dead_kids\n";
    You can also just sleep(20) and print @dead_kids. The focus right now should be on getting this to work without the CTL-C complication and then add that later.

    Update: Oh, I would also add use warnings; either by that statement, or a -w in the hash bang line. This has nothing to do with your current woes, but there are run time checks with warnings enabled that are useful. I leave them on unless some rare, very rare performance or other reason indicates otherwise.

    Another Update: Was able to test some code...for some reason, when SIGCHLD happens, this causes the sleep to end. I don't know why. So there is a loop to restart the sleep every 1 seconds. try this code...have to run to an appointment...oh, exit 1 was caused by missing parens in while statement in the reaper. The sleep issue is the real puzzle here.

    update: added readmore tag - updated code in later post

      Thank you very much. The normal condition works now where it is able to monitor the child process.

      But the ctrl-c condition where I press the ctrl-c does not seem to be working. Maybe my testing is just wrong, but when I press ctrl-c, I do not see any other running child process.
      while (@dead_kids < scalar @all_kids ) { print "dead kids: ", scalar @dead_kids, "\n";; sleep 1; # This is where I want to test the ctrl-c. sleep 5 if ( @dead_kids > 1 ); }
        Ok, I'm back from my meeting and wrote some more code for you.

        Some changes:
        - while ( sleep(1), @dead_kids < @all_kids) {} will wait until the number of dead_kids is the same as all_kids. note: scalar in front of @all_kids is redundant. I've used both of these arrays in a scalar context. The sleep() is so that we don't burn CPU while looping. Handling SIGCHLD seems to prematurely end the sleep, but this code restarts the sleep if we aren't done. In Perl I think sleep() is implemented in terms of alarm() and I will admit something a bit weird is going on here that I don't fully understand although my workaround appears to be fine.

        - the $SIG{INT} = 'IGNORE'; in the child code is important. If you leave it in there, then hitting CTL-C will print the the currently running kids and then resume normal operation. If you comment that line out, then normal CTL-C handling is in effect for the children and they will exit. The CTL-C handler is only installed for the parent. The child either ignores CTL-C or has normal handling for it. On my Linux test machine, when this code is running in the foreground, everybody (all kids and the parent) get CTL-C if I hit that.

        Interesting problem. Have fun with this code. I didn't worry about efficiency in sub ctlc - I think the main point here is how to handle the signals and what to do when.

        #!/usr/bin/perl -w use strict; use POSIX qw(:sys_wait_h); $SIG{CHLD} = \&REAPER; $|=1; #turn off STDOUT buffering my (@all_kids, @dead_kids); my @array = qw(a b c d e f g h); for (1 .. 10) { die "Bad Fork! $!\n" if ( !defined(my $pid = fork()) ); if ($pid ==0) #I'm child { $SIG{INT} = 'IGNORE'; #### comment out to exit on CTL-C my $t = int(rand(5))+1; print "@array sleeping $t secs\n"; sleep ($t); exit (0); } push (@all_kids, $pid); #I'm parent print "Started another child process - $pid.\n"; } print "All child processes have started...\n"; print "kids are: @all_kids\n"; $SIG{INT} = \&ctlc; #CTL-C handler in parent only while ( sleep(1), @dead_kids < @all_kids) {} #wait for kids to finish print "dead kids: @dead_kids\n"; print "All child processes are finished\n"; sub REAPER { my $pid; while ( ($pid = waitpid(-1, WNOHANG)) > 0) { print "Process $pid exited.\n"; push @dead_kids, $pid; } } sub ctlc { print "***** Ctrl-C Trap\n"; my %dead = map{$_ => 1}@dead_kids; print "still alive: "; foreach my $kid (@all_kids) { print "$kid " if !$dead{$kid}; } print "\n"; print "***** Ctrl-C Trap end\n"; }