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

Hello Monks,
I have not been here for ages - someone has been trying to convince me to move from perl to python. But, alas, I'm back!

- can't keep a good language down!

Ok, so here's the problem.

I have a program that needs to fork 3 children to do a job. On the children dying, I need to update a memory cache variable. I do this with a $SIG{CHLD}.

The $SIG{CHLD} signal handler:
sub childDone { # Resource is freed up so place it back in the queue to be used fo +r future toasts. # $SIG{CHLD} = \&childDone; my $chld; $chld = waitpid(-1, WNOHANG) until $chld; return unless $chld > 0; #print "\n$$ sees $chld is done"; system(`touch /tmp/child.$chld`); # Toasting using this resource is now done my $str="toasting." . $jobs{$chld}; $cache->set($str,0); #print "\tresource " . $jobs{$chld} . " is free again!\n"; push @resource, $jobs{$chld}; #print "The resources to use are: @resource\n"; delete $jobs{$chld}; alarm 0; # send SIGALRM so sleep() wakes up }; $SIG{CHLD} = \&childDone;
This all seemed to work fine, but when I installed it on a new machine (with a newer Linux kernel), it seemed to stop working.

What actually stopped working is that the children did not all call the signal handler when they died.

Below is the code that is responsible for forking the children:

@resources = {cdrw0 cdrw1 cdrw2} my $chld; while (@thisround) { my @sort_resource = sort(@resource); @resource = @sort_resource; while (@resource) { last unless @thisround; my $resource = shift @resource; my $data = shift @thisround; if (($chld = fork()) > 0) { # parent code # % jobs holds pid -> resource $jobs{$chld} = $resource; } else { # child code runcmd($resource, $data); exit; # end chld, trigger $SIG{CHLD} } } sleep until @resource == $num_resources; last unless @thisround; } sleep until keys %jobs == 0; # wait for all jobs

Now, within the subroutine

runcmd
, I call the system command to do the job. I have tried the following:
A)  system "cdrecord -immed -dummy -eject gracetime=$gracetime  -tao dev=$resource $distroDirs{$distro}/$data > /tmp/cdrec.$$ 2> /dev/null";
Only the first of 3 children seem to call the $SIG{CHLD}.

B)system "cdrecord", "-immed", "-dummy", "-eject", "gracetime=$gracetime",  "-tao", "dev=$resource", "$distroDirs{$distro}/$data";
Here the signal handler is called fine, but the problem is that the "cdrecord" is producing output that is really screwing with the look of the webpage displaying the results.

C)

my $pid = open (SAFEKID, "-|"); if ( $pid == 0 ) { open STDERR, ">/dev/null"; exec("cdrecord", "-immed", "-dummy", "-eject", "grace +time=$gracetime", "-tao", "dev=$resource", "$distroDirs{$distro}/$da +ta"); } else { my @output = <SAFEKID>; close SAFEKID; }

This one also does not work. It seems to run the command, but never calls my signal handler when it completes. (On another topic, I now have a second signal handler here; how does perl distinguish between the death of this child (hence calling this signal handler) and the death of one of my 3 forked children, where I'm wanting to call my custom signal handler).

Why should these 3 commands work so differently?

How do I rework this so that I call the external program but discard the output, and still end up calling my custom signal handler when the child dies?

I have "cribbed" most of this from the Perl Cookbook and others, so if it looks as if I don't know what I'm talking about then looks are probably not that deceiving.

Any help would be a great relief to me, my family (they've had to put up with all the cussing) and of course my boss.

Thanks.

Open Source - Where everything is QED

Replies are listed 'Best First'.
Re: Invoking an external command - differences
by Roy Johnson (Monsignor) on Feb 07, 2005 at 19:43 UTC
    I don't think you need a signal handler with "system". When it returns, the child has ended, and you can just call the childDone sub yourself. Similarly, when SAFEKID closes, it's done.

    I might be wrong, but I think you only get a SIGCHILD when the child is running in the background.


    Caution: Contents may have been coded under pressure.
Re: Invoking an external command - differences
by RazorbladeBidet (Friar) on Feb 07, 2005 at 20:36 UTC
    Try changing your waitpid test to:
    do { $child = waitpid(-1,WNOHANG) } while ( $child <= 0 );
    and you might want to add a print statement in front of the "return" in your SIG handler to see if waitpid might be returning -1 for those other kids.

    Update:
    I completely overlooked this, but system does a fork call of its own, meaning that your SIG handler should catch those as well - so it really shouldn't be necessary to fork children who fork a single child... just catch the CHLD from the system call (?) (Nevermind, I see you're wanting to capture the PID for the resources hash)

    I guess try the change in the loop and let us know what happens. That seemed to help my program (unless Linux's waitpid/wait4 is that much different than AIX (crosses fingers))