Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

making fork() a flag, and wait()ing properly

by c (Hermit)
on Feb 08, 2002 at 16:03 UTC ( #144124=perlquestion: print w/replies, xml ) Need Help??

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

A while back, I wrote this asking about how to properly fork(). I'm still getting comfortable with the process, but I think I understand it better now than I did then. My new dilemna is that I would like to make fork, an option set by a getopts flag. I managed to get it to work, but it is, in my opinion, ug-ly. I'm also scratching my head about the proper position for wait() and where it should really sit within the script to operate correctly. I can summarize my code as follows:

## declare a pid for forking my $pid if ($fork); ## for my $node(@devices) { ## fork new process if child does not have pid if (($fork) && ($pid = fork)) { next; } elsif((defined($pid)) || (!$fork)) { ##do some exciting things ... ## explicitly exit the child process exit if ($fork); ## if forking error exit script } else { die "\n\nfound errors in forking\nError: $!\n\n"; ## end of if loop } ## wait on child processes to exit wait() if ($fork); ## end of for loop } ## exit the script exit;

use strict and -w are in there, i just wanted to give the general snippet of where my fork() takes place. i'm confused about the multiple exits. I'm not sure that I really need the one within the if loop, however it seemed like I had read an explicit exit should be given to child processes. And again, the position of the wait() statement is still questionable in my mind as to whether is placed correctly to avoid zombie children. And finally, the whole ($fork) flag just seems ugly to me. Perhaps it can be simplied?

thanks! -c

Replies are listed 'Best First'.
Re: making fork() a flag, and wait()ing properly
by bluto (Curate) on Feb 08, 2002 at 16:46 UTC
    A minor nit: your declaration of pid should probably just be
    my $pid;
    ... since I'm not sure what the 'if' buys you.

    While you can do what you'd like with your code, I'd personally move the child code into a separate subroutine and then have a big 'if ($fork)' at the beginning that called it in the fork loop and in the else clause, in order to keep the forking code as easy to read as possible -- YMMV.

    Other than that, your code looks ok up until the wait() (i.e. you do need an explicit exit in the child code). You'll probably want to do your waiting outside of the forking loop, in a loop of it's own. You'll want to call wait() one time for each time you fork. Something like...

    while ($childcnt > 0) { wait(); $childcnt--; }
    Of course you'll need to keep track of the number of children running by incrementing $childcnt each time you perform a fork in the parent code (i.e. right before the 'next' line).

    FWIW, in your code if the fork fails you 'die' right away. I'd probably just report an error, drop out of the loop, wait for all of the children to die, and then die. Otherwise, your existing children will continue to run without a parent (perhaps you want it to do this?).

    bluto

Re: making fork() a flag, and wait()ing properly
by lestrrat (Deacon) on Feb 08, 2002 at 17:59 UTC

    Wouldn't having a wait() inside the loop make the code execute as if it wasn't forking? consider this simplified version of your script:

    ## simplified code, so no error checking... foreach my $node (@devices) { my $pid = fork(); if( !$pid ) { ## child... exit 0; } ## parent wait(); }

    So this means that for each node, you fork off a process, and immediately afterwards the parent waits for that process. The parent will block until the child is done. Thus no merit of forking here...

    You need to do something like this:

    use POSIX; $SIG{CHLD} = sub { while( ( my $pid = waitpid(-1, POSIX::WNOHANG()) ) > 0 ){ ## cleanup code... ## you probably should save the forked ## pids and make sure that the result of ## waitpid is indeed your child } } foreach my $nod ( @devices ) { my $pid = fork(); if( !$pid ) { ## child... } }

    Now, as for your switching needs, I might do something like this:

    use POSIX; my $do_fork = 1; $SIG{CHLD} = sub { while( ( my $pid = waitpid(-1, POSIX::WNOHANG()) ) > 0 ){ ... } } foreach my $node ( @devices ) { if( $do_fork ) { my $pid = fork(); if( !$pid ) { do_exciting_things(); exit 0; } } else { do_exciting_things(); } }

    This is obviously just a psuedocode, so make sure to put plenty more error checking and all that stuff.

      Nice! Your last example of:

      foreach my $node ( @devices ) { if( $do_fork ) { my $pid = fork(); if( !$pid ) { do_exciting_things(); exit 0; } } else { do_exciting_things(); } }

      looks very much like my most recent rewrite after starting this thread, so i am very encouraged that I am actually starting to understand what it is you dudes actually are talking about :-)

      maybe though you could clear up the fog for me on this:

      use POSIX; my $do_fork = 1; $SIG{CHLD} = sub { while( ( my $pid = waitpid(-1, POSIX::WNOHANG()) ) > 0 ){ ... } }

      this one has me scratching my head. if you'd rather point to a perldoc reference, i'm up for that as well. it looks as though you're defining a subroutine as a hash value, but i'm not certain where/how its being called.

      thanks -c

        Are you asking me about just the syntax? or about signal handling with %SIG ?

        As far as the syntax goes, my code is equivalent to something like this:

        my $coderef = sub{ .... }; ## now $coderef is a reference to a subroutine, ## which can be executed like this: ## ## $coderef->(); # $coderef->( @args ) ## $SIG{ CHLD }= $coderef;

        So that means you can actually create an anonymous subroutine, and assign it to a scalar. Which in turn can be assigned to a hash value :)

        As for the signal handling portion, perldoc perlipc will give you more information. Not sure what is the best resource for that... ( I could try to explain, but I'm not confident that my knowledge is 100% correct )

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2023-06-01 07:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?