Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

forking through a subroutine

by jptxs (Curate)
on Apr 09, 2001 at 16:52 UTC ( [id://70987]=perlquestion: print w/replies, xml ) Need Help??

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

I want to give my program the ability to fork off a child at will. So I have a sub that forks when you call it. but it seems that, unlike when I use the standard child then parent in an if/else statement, when the parent in the code dies the kids do not go with it. I can write a clean up routine (will do that either way I imagine), but would like to understand why this is the case or if I'm doing something grossly wrong and it's a result of that =) code for those who havn't stopped reading this yet (this is still just a prototype):
#!/usr/bin/perl -w use strict; print 'do it? '; my $input = ; print 'doing it, sir', "\n"; forkin(); # make sure child is running and healthy system('ps -ef | grep fork'); # make sure parent can still function print 'if you see this all is well', "\n\n"; exit; sub forkin { my $child; die "Can't fork: $!" unless defined ($child = fork()); if ($child == 0) { # i'm the child! &dummy(); exit; } } sub dummy { # when i had only one op here, child went defunct right # after it completed sub hence the loop. I've used loops # like this before in kids and they still have always # dies with the parent... while (1) { print 'I am the child', "\n"; sleep 3; } }
"A man's maturity -- consists in having found again the seriousness one had as a child, at play." --Nietzsche

Replies are listed 'Best First'.
Re: forking through a subroutine
by merlyn (Sage) on Apr 09, 2001 at 18:09 UTC
    I don't understand your phrase:
    unlike when I use the standard child then parent in an if/else statement, when the parent in the code dies the kids do not go with it.
    Because the standard behavior does not require the kids to die if the parent dies. They are independent processes.

    Perhaps you're confusing the results you get when you press ^C, because that signal is sent to all processes, resulting in all processes dying. But if the parent merely exits, the children continue.

    To kill off the children when the parent exits, add a END{} block that signals the list of process ID's saved from each fork into a global array. So your fork routine would push @kids, $child and the end routine would kill 15, @kids.

    -- Randal L. Schwartz, Perl hacker

      "Perhaps you're confusing the results you get when you press ^C"

      on the head as usual, merlyn. I'm still pretty new to the forking code thing and I guess I just never had an instance where the child had an oppurtunity to go on after the parent. Either they all died on the same event or I was ^C-ing them during testing. I was already thinking I needed to write a clean up routine, now I know I have to - and, thanks to your post, I have an idea of how to write it well =) Thanks!

      "A man's maturity -- consists in having found again the seriousness one had as a child, at play." --Nietzsche
Re: forking through a subroutine
by premchai21 (Curate) on Apr 09, 2001 at 19:17 UTC
    Or, if you want to wait for the child to finish, rather than killing it off as the parent dies, then you can, rather than killing the children, use wait or waitpid, which are both documented in perlfunc.
Re: forking through a subroutine
by daniell (Sexton) on Apr 10, 2001 at 01:28 UTC
    You might want different behavior if the parent is just done, or if you've gone and SIGINTed (control-c) the parent. For This is wrote a little set that had some useful code in it:
    • All childs forking record their PID as a key into a hash called scythe. The values are pointers to functions that should be performed for cleanup.
    • It has a reaper function attached to the SIGCHLD to handle the death of a child. This collects its exit value, and passes it as an argument to the function that was stored for cleanup.
    • There's a bait function that does not cleanup but is a valid function reference for a child to have a value in the scythe.
    • There's an event on SIGINT that kills child processes which remain before exiting. Otherwise you could waitpid on all the keys in scythe.
    • A function named phork will setup a child that doesn't need special post processing and execute the args as a unix command. (this is not so useful for your dummy function, but it can be used as an example of setting up a nice child process)
    The code follows:
    use POSIX qw(:sys_wait_h); use IO::Handle; my %scythe; # a scythe for the REAPER (see FAQ); $SIG{CHLD} = \&REAPER; # set handling of zombie child process +es. STDOUT->autoflush; # make sure output is flushed before f +orking # setup an interupt handler # kills every pid used as a key in %scythe #################### $SIG{INT} = sub { print "\nCleaning: "; print join(", ", keys(%scythe)); print "\n"; kill('INT', keys %scythe); print "done with: "; print join(", ", keys(%scythe)); die "\n"; }; #### MAIN #### #### END MAIN, begin defs #### sub phork { # we will call fork and return a pid. The child will exec with all +args # and suppress the child's output; my $pid; if ($pid = fork) { # fork the process; #parent return $pid; }else { #child die "CANNOT FORK!!\n" unless defined $pid; close(STDOUT); # suppressing output close(STDERR); # suppressing output {exec(@_);}; # calls exec with current @_ exit(1); # exec may maybe fail... maybe +. } } sub baitKiller { # This functions is a dummy to be used to be added to %scythe for a $ +pid # This way when the interupt is called, the process with the $pid wil +l # be cleaned up (killed) so it doesn't keep running after its parent' +s # death. If you'd rather it did, just don't assign anything in %scyt +he # for the key of it's $pid. return "Come get some!"; } sub REAPER { # we reap child processes, and post process it. # note: this sub relies on a global %scythe # sort of... if a $scythe{$pid} is defined, it will assume that its + a # reference to a function that takes $pid and $exit_value as args # and presumably does something useful with that. Then it deletes + the # $scythe{$pid} hash entry. is it not nifty? # local vars my $pid; my $exit_value; $pid = waitpid(-1, &WNOHANG); if($pid == -1) { # no child waiting } elsif (WIFEXITED($?)) { # the process exited, get exit value. my $slot; my $state; $exit_value = $? >> 8; if (exists($scythe{$pid})) { $scythe{$pid}->( $pid, $exit_value ); delete($scythe{$pid}); } else { # we're reaping something we didn't sow or didn't care about; return; } } else { # false alarm on $pid } $SIG{CHLD} = \&REAPER; #reset signal handling. } #end sub REAPER
      Hi,

      I'm writing something similar as well at this moment which
      might come in handy.

      You can pass a reference to a sub and it's parameters to the
      spawnChild sub, which will provide you with it's PID and
      a filehandler to the child.

      It's all still very basic and nothing fancy, that's because I'm
      doing lots of learning and testing right now ... but it works ;-)

      #!/usr/bin/perl -w use strict; # # Declaration of Subroutines # sub spawnChild(@); # # Main # my ($pid,$fh)=spawnChild(\&Count,10); print "$pid\n"; # # Subroutines # sub spawnChild(@) { my $childSub=shift; my @Parameters=@_; my ($Cnt,$pid)=0; do { $pid=open FH,'-|'; unless (defined $pid) { warn "Cannot fork: $!\n"; die "Could not fork\n" if $Cnt++ > 5; sleep 10; } } until defined $pid; if ($pid) { #Parent return($pid,*{FH}); } else { #Child; &$childSub(@Parameters); exit(0); } } sub Count($) { foreach(1..shift) { print "$_\n"; sleep(1); } }

      Bye, Leon

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://70987]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2024-04-23 17:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found