fork'ing your world

The fork function is a very powerful tool used among many languages. Unfortunately It's not that common among Perl scripts, maybe because most scripts don't really have a need for it. But it's a handy trick to keep inside your hat.

Fork creates a new process running the same program, usually the process that calls the fork is named the parent process, and the created process is named the child process. The fork function returns 0 to the child process, and the newly created process pid to the parent. Using fork can be as simple as:

print "($$) hello\n";
my $pid = fork;
if ($pid == 0) {
   print "($$) I r child!\n";
}
else {
   print "($$) I r parent!\n";
}


A possible output for running this script can be:

$ perl fork.pl 
(27121) hello
(27121) I r parent!
(27123) I r child!


We can see the different pid for the child process, which is different from the running script pid (the parent). We now have two distinct processes that are running simultaneously, and this can be used to process some tasks that can consume long periods of time, or complex operations that can be divided in smaller operations that can run in parallel. Let's look at a more academic example, the echo server:

use IO::Socket::INET;
my $listener = IO::Socket::INET->new(
                        LocalPort => 9999,
                        Listen => 5,
                        Reuse => 1);

while(my $client = $listener->accept) {
my $pid = fork;
if ($pid == 0) {
handle_client($client);
}
else {
print STDERR "New client, fork'ing (child $pid)\n";
}
}

sub handle_client {
my $client = shift;

$client->send("Hello client, this is an echo server!\n> ");
while(1) {
my $msg;
$client->recv($msg, 100);
$client->send("ECHO from $$! $msg> ");
}
}


First we create a simple socket listening on port 9999, and then start an infinite loop waiting for connections on this socket. Nothing special so far, the beautiful part comes into play when we accept a connection, instead of hanging the script there while we handle the client requests we create a new child process to handle this, and let our parent process continue waiting for new connections and spawning new child's as needed. Let's see it working, the script running:

$ perl echo_server.pl 
New client, fork'ing (child 27379)
New client, fork'ing (child 27381)


And a couple of telnet clients:

$ telnet localhost 9999
(...)
Escape character is '^]'.
Hello client, this is an echo server!
> hello echo server
ECHO from 27379! hello echo server

$ telnet localhost 9999
(...)
Escape character is '^]'.
Hello client, this is an echo server!
> and another request
ECHO from 27381! and another request


This is great because not only we can handle more than one client at the same time, since several child process can be working simultaneously but also we don't make the new clients wait for the previous client to finish processing. A client can be very slow, and have a huge amount of requests to process, but we are accepting new connections and processing more requests at the same time.

Hope I haven't bored anyone to death with this post. Happy fork'ing.

Leave a comment

About smash

user-pic I blog about Perl.