Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

A Quick fork theory question

by E-Bitch (Pilgrim)
on Jun 14, 2001 at 21:20 UTC ( #88513=perlquestion: print w/replies, xml ) Need Help??

E-Bitch has asked for the wisdom of the Perl Monks concerning the following question:

I am unfamilar with perl's use of fork... I know the basic concepts of a forked process, but need to know an implementation thing, can you do the following: fork a variable number of processes (based upon the results of a find routine) where each forked process does another find routine for different items, allow the parent process to continue doing other stuff, collect all of the $pid from the forked processes (when created), store them in an array, and waitpid() on their statii before exiting the parent process?

Replies are listed 'Best First'.
Re: A Quick fork theory question
by ZZamboni (Curate) on Jun 14, 2001 at 21:38 UTC
    Of course you can :-) and if you are familiar with how it would be done in C (which I have the feeling you do, from your question), it should be easy to translate the solution to Perl. The semantics of fork and waitpid in Perl are identical to C (this also means that at least for waitpid the behavior might change between different systems).

    On the other hand, managing multiple processes is always tricky, and you have the risk of things running amok if you make a mistake, and it may not be immediately obvious how to properly manage multiple subprocesses. You may want to look at some existing solutions such as Parallel::ForkManager or ForkBlock.

    --ZZamboni

      Maybe I wasnt as informed as I thought... I am trying to run the following code:
      #!/usr/bin/perl -w $|++; print "Hello before fork\n"; #print first message $thiscount = 0; #counter for number of forks for($i = 0; $i < 2; $i++) #for loop to fork 2 procs { $thispid = phork(); #fork a proc, store the pid print "Just forked $thispid\n"; #tell us what you've done $thiscount++; #increment the fork count } $wpid = wait; #wait until children are finished print "i'm waiting on $wpid\n"; #tell us what you've done print "hello from after fork. I have forked $thiscount child processe +s.\n"; #final message from parent? sub phork() #subroutine for forking stuff { $cid = 0; #child id $count = 0; #number of times through. FORK: { print "i am about to start forking stuff. my id is $$, and i +have done this $count times\n"; $count++; #increment the number of times through if($pid = fork()) #parent process { print "Parent phorking child. I am: $$, and my child is $ +pid\n"; $cid = $pid; } elsif(defined $pid) #child processs { print "Hello from in phork. I am $$, and my parent is: $p +id\n"; return $$; } elsif($! =~ /No more process/) #error { sleep 5; redo FORK; } else #error { die "Cant phork: $!\n"; } } return $cid; #ship back the child id }
      my intentions are to fork 2 children, have them print out their process IDs, and return to the main part of the program, and then the main portion waits on the children to finish, then prints out that it too is finished. I get the following results.:
      [33] rk110759@sunray22: test.pl Hello before fork i am about to start forking stuff. my id is 15092, and i have done th +is 0 times Parent phorking child. I am: 15092, and my child is 15095 Just forked 15095 i am about to start forking stuff. my id is 15092, and i have done th +is 0 times Parent phorking child. I am: 15092, and my child is 15096 Just forked 15096 Hello from in phork. I am 15096, and my parent is: 0 Just forked 15096 Hello from in phork. I am 15095, and my parent is: 0 Just forked 15095 i am about to start forking stuff. my id is 15095, and i have done th +is 0 times i'm waiting on -1 hello from after fork. I have forked 2 child processes. Parent phorking child. I am: 15095, and my child is 15097 Just forked 15097 Hello from in phork. I am 15097, and my parent is: 0 Just forked 15097 i'm waiting on -1 hello from after fork. I have forked 2 child processes. i'm waiting on 15097 hello from after fork. I have forked 2 child processes. i'm waiting on 15095 hello from after fork. I have forked 2 child processes.
      Am i being ignorant, and my program is actually printing out what it I intended? and why does the message after the fork ("hello from after fork...") get printed out 4 times?
        First, inside phork(), you incorrectly try to get the parent id from within the child. Going by the Camel, 3rd ed., p. 715, the following change should be made:
        elsif(defined $pid) # Quoth the Elders: # "$pid is zero here if defined" { print "Hello from in phork. " print "I am $$, and my parent is: " . getppid ."\n"; return $$; }
        Secondly, you've already seen the others' comments on the problem with the children re-entering the loop and spawning their own children. Here's my take on what you can do to fix it. (I'm posting this as much to help you as to get told by others if it's a horrible idea, so caveat coder...)
        use strict; $|++; my $parent_id = $$; # Before forking, save the $parent_id my $totalcounts = 0; for(my $i = 0; $i < 2; $i++) # for loop to fork 2 procs { print "[$totalcounts == $i]\n"; # just to check... # if I am the parent... if ($$ == $parent_id) { #...fork a proc and store the pid my $thispid = phork(); } # I have now forked. If I am NOT the parent... if ($$ != $parent_id) { ## Note 'if'. An else/elsif ## WON'T work here! The ## kids would skip this. #...do whatever I want to do, but when done: exit; # Now the children have been stopped # before they could continue along the $i loop. } $totalcount++; #increment the fork count }
        Alternatively, add the exit() at the appropriate spot in phork().

        -- Frag.

        Okay, this may be difficult to follow along to; but I will do my best.

        When you fork, your children processes get an exact replica of the process. So, your first child gets spawned. Now, it has a copy of $i which has the value zero. So, it is spawned, it runs the phork() subroutine, and it returns to the for loop with a $i of 0. So, it beings to execute the loop; and your first child process forks its own child and that child (the first child of the first child of the parent) is spawned and it gets a copy of the memory block who has a $i of 1. This stuff happens some more with the second child of the parent and so on. What you want is something along these lines: (warning: untested)
        #!/usr/bin/perl -w use strict; my $childLimit = shift @ARGV; #get total number of children my @pids = (); #array of pids of children for(my $i = 0; $i < $childLimit; $i++) { if( !( $pids[$i] = fork() ) ) { # in the child callChildSub(); exit(0); # exit from the child process IMPORTANT } } foreach my $pid (@pids) { waitpid $pid, 0; } sub callChildSub() { print "I am the child with pid $$.\n"; }
        Of course, that needs some error-checking and such, but that should get you started.

        Jeremy
        Because your child *returns* (after it has done the printing) into the for loop, thereby (next iteration) phorking itself a child - and 2x2 makes 4. BTW it should exit or die"\n".
Re: A Quick fork theory question
by frag (Hermit) on Jun 14, 2001 at 21:31 UTC

    You might want to check out Parallel::ForkManager. I haven't used this myself yet, but I think that what you can do here is use finish() to return an error code, and run_on_finish() to specify what to do with that error code.

    -- Frag.

Re: A Quick fork theory question
by E-Bitch (Pilgrim) on Jun 15, 2001 at 01:41 UTC
    Okay... I have that figured out, thank you all for your help... Now what I am noticing, however, is that each child process is running in sequence, not concurrently. Is there a way in perl to make the processes run parrellel? I.e. force some sort of concurrency / automatic waiting / context switch(other than polling the cpu and calling a sleep() every few milliseconds)?
    Thanks in advance, EBitch

      Um, fork is what makes the children run in parallel. If that isn't happening here, then your children probably aren't doing much (since you aren't using wait or waitpid, which can be used to make the parent wait children). Doing some trick to make the children to appear more like they are running in parallel will just slow things down.

      If you just want to see that they are running in parallel, then insert a sleep 5 in your child code.

              - tye (but my friends call me "Tye")
      One possible solution is to use something like Proc::Simple to handle all your forks as nice simple objects.
      In your parent body, install a sig handler that traps, say SIGUSR1.
      On a child death, the child 'kills' the parent with SIGUSR1, making it wake up from a lengthy sleep, or other work, and take care of the signal in whatever way you want.
      I'd use SIGUSR1, as I may be doing other things that invoke pipes, and that keeps invoking the SIGCHLD handler, if I install that. Which gets messy.
      At least with SIGUSR1, you can be pretty sure it's what you want sending the signal.
      In Proc::Simple, you simply iterate over an array of process objects to see which ones have died and how.

      Hope this helps a little,

      Malk
Re: A Quick fork theory question
by Anonymous Monk on Jun 14, 2001 at 21:25 UTC
    Yes.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (7)
As of 2020-07-13 08:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?