Hi,

First, I have some comments on your design. Later on I offer an alternative approach you might find easier to use.

Your children should exit when they are done, otherwise each one of your fork-ed children will begin executing your "main" program code after your initial for loop.

Next, you don't really want to call waitpid in a loop like that waiting on each child sequentially since children may exit (or terminate unexpectedly) in any order. If you don't reap children as the parent continues to wait or do further processing, you'll accrue zombie processes that won't finish existing until you wait on them.

I'd encourage you to learn about how to "reap" finished children. The perldoc page perlipc has a section titled Signals that talks about signal handling, and specifically the CHLD signal I'll use below.

If you look at the code below, I'll walk you through what it's doing briefly.

  1. A SIGCHLD handler is defined to call the reap_kids() routine (discussed later.)
  2. A hash called %kids is created, which will contain child PIDs as keys. These can be added/deleted in any order (unlike an array, which we'd have to reference by index.)
  3. Children are launched, similar to your sample code. However, notice this time that the child side makes sure to exit when it is finished.
  4. The main code now prints that it is waiting, and goes into a sleep loop as long as at least 1 child is still around.
  5. Here's where the magic happens. The reap_kids() subroutine gets called when the kernel informs the parent process one of the children has finished running. This may be a normal or unusual termination, and you would have to check the exit status to get more information. (see the $? definition in the perlvar docs for specifics.)
  6. The dispatch_child() routine is what the children "do" in this code. Here they simply say the ID (from the main loop near the top of the code), sleep a random number of seconds, then say goodbye.

Something to notice about this code is that I have intentionally made the sleep random, so children exit in a random order. If you run this program multiple times, you'll see that the children do not necessarily end in the same order, and won't usually end sequentially. This is why it's wise to use signal handling to reap any children that have finished.

Here's the code:
use strict; use warnings; require POSIX; # provides WNOHANG # Set up a signal handler. # This prevents child processes from becoming zombies. # The subroutine is defined further below. $SIG{CHLD} = \&reap_kids; # Track spawned child processes. # This is a hash, keyed by the PID. # We'll use this later to mark them as finished. my %kids; for my $child_id (1..3) { my $pid = fork(); # PARENT: track kid if ($pid) { $kids{$pid} = 1; } # CHILD: dispatch to child-routine below. # Explicitly exit, in case the child code neglects. else { dispatch_child( $child_id ); exit(0); } } print "Main code now waiting for all children.\n"; # Wait for all children to finish. # The signal handler, reap_kids(), catches finished children. # Just continue to sleep if there are pending processes. while( scalar(keys %kids) > 0 ) { sleep 1; } # That's all the main code does. print "All done with main code.\n"; exit(0); # --- # Suborutines # --- # Child reaper. # This will be called when the kernel tells us a process has # finished running. It's possible more than 1 has done so. # We run this in a loop to reap all children. sub reap_kids { local $!; # good practice. avoids changing errno. while (1) { my $kid = waitpid( -1, POSIX->WNOHANG ); last unless ($kid > 0); # No more to reap. delete $kids{$kid}; # untrack kid. } } # Child dispatch code. # Here is where you write what your child does. # Ideally it should exit, but we enforce this above anyway. sub dispatch_child { my $id = shift; # passed from caller. print "Hello from child number $id\n"; # Sleep for a random number of seconds. # Between 5 and 10. my $seconds = 5 + int(rand(6)); sleep $seconds; print "Goodbye from child number $id\n"; # Be nice and explicitly exit: exit(0); }

In reply to Re: How to make parent wait for all the child processes. by Apero
in thread How to make parent wait for all the child processes. by gjoshi

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.