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

I've been playing around with fork for the last couple of days and I think I have a pretty good grasp of how a "plain" fork works. It creates a copy of the script and executes the copy. Thats all well, good and simple, but how does that extend to situations like so:
while(my $client = $server->accept()) { next if fork; # random # client # handling # stuff exit; }
Obviously I know it works and does what it's supposed to do, but how does the fork work in context of the while loops? It seems like its trying to goto the middle of a while loop, which I thought was impossible. Can anyone help me out here?

Replies are listed 'Best First'.
Re: Forking and loops: A plea for understanding.
by Zaxo (Archbishop) on Aug 31, 2003 at 19:06 UTC
    It creates a copy of the script and executes the copy.

    That may be what's bothering you. The trick is that both the parent and child go on from the same point, that where fork is called. The child gets a private copy of all the parent's environment, stack, memory and everything. It is said that fork returns twice, once in the parent and once in the child. The return value of fork is the only difference between the two, besides the dynamical process table variables like $$.

    Following the parent only, your server loop looks like

    while(my $client = $server->accept()) { next; }
    A child ignores the next line and goes on to do the client handling and exit. That ends the program as far as the child is concerned.

    Two points which you may not need to be reminded of. The fork call can fail, returning undef, leaving you in the parent but executing child code. When you hit exit then, your server quits.

    Second, you may be spinning out lots of zombies. In a persistent server, it is probably easiest to set $SIG{CHLD}='IGNORE'; if your OS supports that.

    Parallel::ForkManager is a good wrapper for this sort of thing.

    After Compline,
    Zaxo

Re: Forking and loops: A plea for understanding.
by liz (Monsignor) on Aug 31, 2003 at 18:40 UTC
    The fork() doesn't know anything about the while loop. It simply copies the current process completely and returns the pid to the original process and 0 to the child process.

    In your example, that means that it reloops in the original process and executes the rest of the loop in the child process. If there wouldn't be an exit() at the end of the loop, then the child would also start at the beginning of the loop again.

    Maybe it's clearer written like this:

    while(my $client = $server->accept()) { my $pid = fork; if (!defined $pid) { die "Could not fork()\n"; } elsif ($pid == 0) { # random # client # handling # stuff exit; } }
    Liz
      I understand that it creates a copy and returns 0/pid depending on which branch you are in. I also understand that the parent just continues the loop while the child exits at the bottom. My question was "how is this possible"? How can perl create a copy and then start executing in the middle of a a loop?
        Ah, ok. Well, Perl cannot do that. ;-)

        The magic in this is basically an Operating System feature, only found on *nixen, which is accessible with the C-function by the same name. Check out man fork.

        So, Perl doesn't almost doesn't do anything to fork itself, it just calls the C-function "fork()" and let's the Operating System do all of the work.

        Well, close. Since 5.6.0, Perl flushes all files opened for output before forking. And on Win32 systems, there is no such thing as a C-function fork(). There some magic is done by Perl, but since I don't know much about Win32 systems, I can't tell you anything about that.

        Hope this clears it up.

        Liz

        How can perl create a copy and then start executing in the middle of a a loop?

        I've to explain this in Unix term. There's no fork() on Windows; it's emulated on that OS, but I've no desire to know how.

        First, it's not a Perl thing, it's an OS thing. The Perl fork() just does some handy stuff for the programmer (like flushing buffers) and then calls the system's fork that does all the neat stuff. Second, to understand how fork works, you first have to understand a little what consists of a process.

        A process occupies several parts of the memory. There's the text segment, which is where the (binary) program is stored, and there's the data segment which contains the variable data of a process. This is the part that changes during the lifetime of a process. (Lots of handwaving here, in reality most OSses have more segments, but that's not important now). There's also a program counter, a little pointer that's used to keep track where the process is in the execution of the program. For sake of the argument, consider the program counter to be in the data segment. Note that for the case of a Perl program, it's perl, the binary, that is in the text segment, and the (compiled) Perl program that's in the data segment - so the entire Perl program is copied.

        Now, what happens at a fork? The OS creates a new process; the child. The child *shares* the text segment, but the data segment is copied. (The latter is usually implemented using copy-on-write, but that's just a technique to delay the copying, the program won't notice it). This includes the program counter! So, both the child and the parent continue at the same point in the program after a fork. Only the return value is different. Perl doesn't start executing in the middle of a loop - it was still there!

        Note that the above was a simplied version of what really happens. What really happens will differ from Unix flavour to Unix flavour, but many of those details aren't noticeable for most programmers.

        Abigail

Re: Forking and loops: A plea for understanding.
by dws (Chancellor) on Aug 31, 2003 at 18:45 UTC
    It seems like [the fork] is trying to goto the middle of a while loop, which I thought was impossible. Can anyone help me out here?

    Imagine it this way: Look at your code. Pretend that immediately after the fork, a layer of clear paper, representing the child process, has been set down over your code. Both pages have a moveable marker on them, right after the fork. Now, the markers, which represent program execution flow, proceed independently. On the original page, the fork has succeeded and returned a child process id*, so the next if test directs execution back to the while loop expression. On the new clear page, however, the fork as return 0, the next if test fails, and execution proceeds until the exit is reached. The fact that you're in a loop is incidental.


    *Two problems here: First, it's possible for fork() to return undef if it wasn't possible to fork. Second, if fork() does succeed, you'll want to save the child process so that you can reap it when the child process exits. See perlipc in the on-line Perl docs for the full story plus examples.

Re: Forking and loops: A plea for understanding.
by Anonymous Monk on Sep 01, 2003 at 03:42 UTC