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

Dear Monks, I've made a very small script to try to make the fork function work the way I would like to... What I found out are two things... 1) The index and the array elements are in sync, so the most important part are ok ! 2) The code executed on the child part won't be printed sequentially or in a deterministic way. I understand that the commands are to be executed in parallel, but I also think ( probably wrong ) that the code within the if (defined...) and exit 0; should be executed as one. In other words, I don't care the order 'A' or 'B' is executed, since it's in parallel, but I would like to have the printing in sequence. So... is this a problem of print buffering or I have the concept wrong ? It's true, I won't get a cursor back until I press enter, but that's a minor issue compared to the rest... :) Best regards, lfm
#!/usr/bin/perl @LIST = ('A','B','C','D','E'); $i=0; for (@LIST) { if ($pid = fork) { if (defined $pid) { $letter= $LIST[$i]; print "$letter is in position $i, with pid $pid\n"; print "That's what I said... $letter is in position $i, with p +id $pid\n"; exit 0; } else {die "Error in forking at $i: $!\n";} } $i++; }

Replies are listed 'Best First'.
Re: deterministic fork
by blakem (Monsignor) on Oct 19, 2001 at 12:20 UTC
    If I understand you correctly, you're expecting the two print statements to follow one right after another for each child. So you'd like the output to look something like:
    D is in position 3, with pid 26031 That's what I said... D is in position 3, with pid 26031 C is in position 2, with pid 26030 That's what I said... C is in position 2, with pid 26030 B is in position 1, with pid 26029 That's what I said... B is in position 1, with pid 26029 A is in position 0, with pid 26028 That's what I said... A is in position 0, with pid 26028 E is in position 4, with pid 26032 That's what I said... E is in position 4, with pid 26032
    Is that right?

    It doesn't work that way because the children are running in parallel, so there is no guarantee that a child will get to print its two lines w/o another child printing anything in between. To better illustrate this, try putting a sleep 3; between the two print statements.... I think that might help you grok whats happening.

    Two side notes: 1.) You really should use strict and 2.) You don't have to hit enter to get a prompt. Your shell is coming back almost immediately... *before* the children are done printing. Therefore, your prompt is getting mixed in with the output of your script. Try entering a command after your program runs to see what I mean.

    -Blake

Re: deterministic fork
by Zaxo (Archbishop) on Oct 19, 2001 at 13:30 UTC

    Update: waitpid added to serialize the kids in example code.

    First, I suggest you enable strict and warnings. Multiprocess code has plenty of capacity to confuse about lexical scopes.

    The order in which child processes execute is entirely up to the OS scheduler. It cannot even be said whether the parent or the child will run first. You are correct that print buffering is an issue in general, but your code is written so that the only printing is done in the parent.

    Your program is actually exiting the parent and the loop is continuing in the child. Is that what you mean to do? That is one reason your prints are getting so out of order. Here's a version which does one print in the parent and the other in the child, with the original parent doing all the forking:

    #!/usr/bin/perl -w use strict; my @LIST = 'A'..'E'; my $i=0; $|++; # Unbuffered print on STDOUT for (@LIST) { my $pid; if ($pid = fork) { # Parent, $pid is true, is certainly defined print "Mother says $_ is in position $i, with pid $pid\n"; # waitpid serializes the kids waitpid $pid, 0; } # $pid is 0 in the child elsif (defined $pid) { print "Child says That's what I said... $_ is in position ", "$i, with pid $$\n"; exit 0; } # $pid undefined -- error else { die "Error in forking at $i: $!"; } $i++; } =pod That's what I said... A is in position 0, with pid 19041 A is in position 0, with pid 19041 B is in position 1, with pid 19042 That's what I said... B is in position 1, with pid 19042 That's what I said... C is in position 2, with pid 19043 C is in position 2, with pid 19043 D is in position 3, with pid 19044 E is in position 4, with pid 19045 That's what I said... D is in position 3, with pid 19044 That's what I said... E is in position 4, with pid 19045 =cut

    After Compline,
    Zaxo

Re: deterministic fork
by alien_life_form (Pilgrim) on Oct 19, 2001 at 13:41 UTC
    Greetings.

    While my honorable brethren are of course totally right, I'd also like to point out that for the sole purpose of getting the example to work according to your expectations you might take advantage of the line buffering behavior of standard output and just print your stuff in one line. Then turn off buffering/change output device and everything goes southwest again.

    The moral is that if you want serial access to a shared resource - this is the general problem you are after - you either:

    • Know darn well that what you are doing is guaranteed to be atomic - much documentation reading usually required.

      OR

    • Create a resource manager process along with a protocol to talk to it (the suggestion about piping amounts to this)

      OR

    • Lock the resource for the time you are acceding it - look at flock and system V semaphores. Then start worrying about deadlocks, startvation and other crazy stuff :)

    Cheers,
    alf

      Dear Monks
      Just want to show my gratitude to all of you who have replied to my question. Thank you for the detailed and expert explanation, aswell as for the sugestions ( strict ; ).

      I have learned a lot and in this example.This site is by far the best Perl - and good programming style - resource I have seen.

      Best regards,
      falcon.

Re: deterministic fork
by MZSanford (Curate) on Oct 19, 2001 at 13:24 UTC
    To start,blakem is completly right. But, i thought i would add to this the concept of how to make sure the printing is done in order.

    One way to have the print statements sorted would be to have one process (the parent), that each other process had a pipe() opened to. Then have the parent read from each child and print it's output. this would hust performace alot

    One other way would be to create a named pipe on the file system (mknod in unix), and have each child direct it's STDOUT and STDERR into the pipe. Then, have another child reading from that, ordering the messages, and printing them to screen.

    Last i can think of is to use shmread() and shmwrite() to pass the information around, and have one process read the messages , sort them, and print.

    While all of these work (as would using UDP or TCP for the message passing), it does seem a bit much. I usually feel that this is not how fork() is meant to work, so it is better to understand that they will simply be out-of-order, and design with that in mind.
    i had a memory leak once, and it ruined my favorite shirt.