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.
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
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |