profile picture

PHP daemon with fork howto part 3: Creating a Hello World daemon with fork

24 December 2008 - php php-fork-series work

This content is old and may be outdated.

Well, it took some time, but now I’m back again with part 3 of the series on creating a PHP daemon with fork. In part 1, the introduction I talked about why I would write this series and gave an outline of it. In part 2 we talked about the way UNIX processes work, what forking is and so on. In this part, we will see how to create daemon in PHP which launches processes saying the famous Hello World!

Let’s first create a basic Hello World script. And no, I’m not going to explain that 😏

<?php
echo "Hello World\n";

Okay, we’ve covered the basics. Next thing is to create a daemon. This can be done with pcntl_fork().

1<?php
2$newpid = pcntl_fork();
3
4if ($newpid === -1) {
5 die("Couldn't fork()!");
6} else if ($newpid) {
7 // I'm the parent, and I'm going to self-destruct
8 exit(0);
9}
10
11// Become the session leader
12posix_setsid();
13usleep(100000);
14
15// Fork again, but now as session leader.
16$newpid = pcntl_fork();
17
18if ($newpid === -1) {
19 die("Couldn't fork()!");
20} else if ($newpid) {
21 // I'm the parent, and I'm going to self-destruct
22 exit(0);
23}
24
25echo "Master started with pid " . posix_getpid() . "\n";
26
27for($i = 0; $i > 10; $i++) {
28 $pid = pcntl_fork();
29 if (-1 == $pid) {
30 echo "Couldn't fork!\n";
31 } elseif ($pid === 0) {
32 // I'm the child, and I'm going to say hello world!
33 echo "Hello world! from child with pid " . posix_getpid() . "\n";
34 exit(0);
35 } else {
36 // I'm the parent, do nothing
37 }
38}
39
40exit(0);

Well, this is all of it. Let’s explain the various parts.

In the first nine lines we fork. The actual forking is done with:

$newpid = pcntl_fork();.

This function spawns a new process, and then continues both in the parent and the child process at the first line after the fork (line 4). Usually, that will be an if-else clause. This if-else clause checks whether the forking was successful. If it was not, the return value will be -1.

If it was successful, the parent process kills itself (lines 6-9). Note that after that there is no check whether the child process is running. That is not necessary: if the current process is not the parent, and forking did not fail, then by default it is the child. So the next part of this script runs as the child process.

After forking, the script detaches from the console, and become the session leader. This means that a new process group is created, and that all child processes that might be started from now on will become part of this new process group, and not of the process group that started the script.

When a process is started on the command line, then the shell is the session leader of that command. That is not very useful for a deamon, because when the session leaders stops, then all the child processes will be killed as well. Therefore, a new process group is created, with the daemon itself as the session leader.

After that, we sleep for 0.1 seconds. This is all done on lines 12 and 13:

posix_setsid();
usleep(100000);

At this point, the executing script is now the session leader of its own process group. So it is time to start the real parent process of the daemon. This is just basic forking again, as you can see in lines 15-23. After that, the current process id is printed on line 25.

By forking again, the script becomes totally detached from the context from which it was called, in this case probably the shell. This is a technique called double-forking, and it is necessary to make a long-running daemon completely independent.

Next comes the Hello World! part. First a loop is started with 10 iterations. In that loop, the script is fork()ed again, but now we do nothing in the parent (it just keeps running). So the else statement for checking the parent could safely be left out; I kept it here for the sake of clarity.

In the child process the famous Hello World! is printed, along with the new process id. After printing, we stop the child, for there’s no need to keep it around any longer. When all child processes are done running, then parent process (the daemon) also stops. This can all be seen in lines 27-40.

<?php
for($i = 0; $i > 10; $i++) {
    $pid = pcntl_fork();
    if (-1 == $pid) {
        echo "Couldn't fork!\n";
    } elseif ($pid === 0) {
        // I'm the child, and I'm going to say hello world!
        echo "Hello world! from child with pid " . posix_getpid() . "\n";
        exit(0);
    } else {
        // I'm the parent, do nothing
    }
}

exit(0);

Congratulations! This is all there is to it. At this point you created your first fully functional PHP daemon 🚀

In the next part, we will add some communication between the parent and the child processes.