PHP isn’t used to write daemons very often, and other languages (like Perl or C) might be more suited to your typical daemon. There are times when PHP is the right choice though, for instance if the rest of your project is a PHP website and you want to keep the same code language across the project. Everything here is available elsewhere online, but I couldn’t find a page that brought it all together neatly (fork, exec, waitpid, signal handling), so here it is.
In this case, I wanted to be able to use the same DB connection file and config file as the rest of the project.
The commands below which start with pcntl (Process CoNTRol) will only work from command line PHP, not from PHP run as an Apache module. Basically you can’t launch a daemon directly from PHP run through Apache.
Forking: Basics of a daemon
The basic thing a daemon must do is get detached from whatever process launched it. Every process except init has a parent process. When a daemon disconnects from its parent process init will adopt it. We can use the pstree command to visually see the process hierarchy.
Detaching from the parent process is done by forking. Forking makes two running copies of your program. They will both be running the same code at the same place. The new branch (the child) will have a new PID, the original branch (the parent) will continue to have its original PID. The pcntl_fork() command will return different values for the parent and child. The pcntl_fork() will return the child’s PID to the parent and will return 0 to the child.
The following snippet will take advantage of this return value difference to make the parent exit.
#!/usr/bin/php
<?
// Daemonize
$pid = pcntl_fork(); // parent gets the child PID, child gets 0
if($pid){ // 0 is false in PHP
// Only the parent will know the PID. Kids aren't self-aware
// Parent says goodbye!
print "Parent : " . getmypid() . " exiting\n";
exit();
}
print "Child : " . getmypid() . "\n";
If you put this in a script and run it, you will see the following output:
$ ./daemonize.php Child : 10248 Parent : 10246 exiting
Notice that they have different PIDs. If we could’ve seen the parent PIDs, we would’ve seen that the child PID was now a child of init (PID 1). We’ll be able to see that with the next code sample.
A Featurefull PHP Daemon
Really, the above script daemonizes so we could call it good and be done, but it’s not that useful yet. We’ll add a few more features and end up with something we can actually use.
Daemon Feature : Loop Forever
Most daemons keep running and either keep doing something, or waiting for a signal of some sort and then doing something. In the code below we’ll add a while(TRUE) loop so we’ll never exit.
Daemon Feature : Close handles and Change directory
We want to close any files we don’t need so that we don’t get in anyone’s way. By default the directory you’re in when you run a process is kept open by the program. as the CWD (current working directory). If you wanted to unmount the drive that path is on, umount might complain about open files.
Closing STDIN, STDOUT and STDERR is a good idea, because they’re going to disappear when you close that terminal anyways. Remember once you’ve closed them, don’t print anything — it’ll throw an error and your daemon will die.
Daemon Feature : Exec Worker Processes
It is often convenient to have a daemon be a simple controlling program which launched worker processes to do the heavy lifting. The daemon I wrote watch a user table in a database. When certain user criteria were met, the daemon launched a worker process to do some batch processing for that user. This is represented in the code below by the “if(count($pids) < 6)”, that’s our condition here.
When the condition is met, we fork again. This time, the parent process sticks around (the ‘else’ part of the if(!$pids) statement) while the child proces execs a worker process (worker.php). Exec replaces the currently running process so in theory we don’t need to worry about exiting the child. We keep the exit() around though in case launching the worker process fails for some reason.
Daemon Feature : Good Parenting (waitpid)
Every time we fork and exec we get another child. Those child worker processes won’t run forever and we want to avoid Zombie processes, so we need to reap them when they’re done. There are two ways to handle this situation. The simplest way is to tell our daemon to ignore SIGCHLD signals like so:
pcntl_signal(SIGCHLD, SIG_IGN);
If we are ignoring SIGCHLD, the child processes will be reaped automatically upon completion.
The other option is to use waitpid to reap children that have finished. In the example below we use pcntl_waitpid with the -1 and WNOHANG arguments. -1 tells pcntl_waitpid to reap any child which has exited. WNOHANG tells it to return immediately even if no child has exited yet. Assuming a child has exited, pcntl_waitpid will return the PID of the process which exited.
We collect our child PIDs as we fork and save them in $pids. The pcntl_waitpid loop removes pids that have exited, so $pids should always have a list of child PIDs that haven’t exited yet.
Daemon Feature : Shutting Down Cleanly
We’ll use the signal handling discussed yesterday so we can quit on demand instead of needing to be killed. One thing we’ll do is iterate over any remaining child PIDs and send them the same signal we were sent. This allows them to also shut down cleanly. Once each of the child processes has closed then we exit the daemon.
Brining it all together : PHP Daemon Code
The code here along with the code from the signal handling post should give you everything you need to get going. Have fun!
#!/usr/bin/php -q
<?php
ini_set('display_errors',0);
print "Parent : ". getmypid() . "\n";
global $pids;
$pids = Array();
// Daemonize
$pid = pcntl_fork();
if($pid){
// Only the parent will know the PID. Kids aren't self-aware
// Parent says goodbye!
print "\tParent : " . getmypid() . " exiting\n";
exit();
}
print "Child : " . getmypid() . "\n";
// Handle signals so we can exit nicely
declare(ticks = 1);
function sig_handler($signo){
global $pids,$pidFileWritten;
if ($signo == SIGTERM || $signo == SIGHUP || $signo == SIGINT){
// If we are being restarted or killed, quit all children
// Send the same signal to the children which we recieved
foreach($pids as $p){ posix_kill($p,$signo); }
// Women and Children first (let them exit)
foreach($pids as $p){ pcntl_waitpid($p,$status); }
print "Parent : "
. getmypid()
. " all my kids should be gone now. Exiting.\n";
exit();
}else if($signo == SIGUSR1){
print "I currently have " . count($pids) . " children\n";
}
}
// setup signal handlers to actually catch and direct the signals
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");
// All the daemon setup work is done now. Now do the actual tasks at hand
// The program to launch
$program = "worker.php";
$arguments = Array("");
while(TRUE){
// In a real world scenario we would do some sort of conditional launch.
// Maybe a condition in a DB is met, or whatever, here we're going to
// cap the number of concurrent grandchildren
if(count($pids) < 6){
$pid=pcntl_fork();
if(!$pid){
pcntl_exec($program,$arguments); // takes an array of arguments
exit();
} else {
// We add pids to a global array, so that when we get a kill signal
// we tell the kids to flush and exit.
$pids[] = $pid;
}
}
// Collect any children which have exited on their own. pcntl_waitpid will
// return the PID that exited or 0 or ERROR
// WNOHANG means we won't sit here waiting if there's not a child ready
// for us to reap immediately
// -1 means any child
$dead_and_gone = pcntl_waitpid(-1,$status,WNOHANG);
while($dead_and_gone > 0){
// Remove the gone pid from the array
unset($pids[array_search($dead_and_gone,$pids)]);
// Look for another one
$dead_and_gone = pcntl_waitpid(-1,$status,WNOHANG);
}
// Sleep for 1 second
sleep(1);
}
Warnings / Notes:
If you daemonize, close your terminal, and then your daemon tries to print, you’re going to have problems. Your daemon will still try to print to STDOUT, which will now be closed. This will likely cause it to fail in an unpleasant manner.









hi, i found this page extremely useful for spawning worker threads. exactly what i needed. however, i don’t quite understand the first line of your script
#!/usr/bin/php -q
What’s -q? I’ve looked in man docs and did php -h to see what pop up… I don’t see this switch listed anywhere for PHP. Could you kindly explain?
Thanks!
The PHP man page (for PHP 5.3.3) I have on Ubuntu says this:
-q Quiet-mode. Suppress HTTP header output (CGI only).The -q might not actually be needed for daemons etc. anymore. On an older version of PHP I would get the HTTP headers when I used PHP from the command line, but even without the -q I’m not seeing that anymore. It must see that it’s command-line PHP and not print them.
If you daemonize, close your terminal, and then your daemon tries to print, you’re going to have problems. Your daemon will still try to print to STDOUT, which will now be closed. This will likely cause it to fail in an unpleasant manner.
I start my children via cron or terminal, and have the above problem.
How can it be fixed?
One option is to make sure that your program never prints anything, then you won’t trigger this issue. Another option, for cron at least, is that you can enter your cron command with >/dev/null 2>&1 at the end. >/dev/null redirect stdout to /dev/null of course, and 2>&1 redirects any error output to stdout (which is going to /dev/null).
Running from a terminal and then closing the terminal may give you one other issue, closing a terminal may send your application SIGHUP which will typically cause a process to exit if it is not handled.
Your code is very useful. However, I want to let the parent process know the exit status of children. e.g. The child exit with an integer like exit(0) or exit(1), etc. Is it possible for the parent to catch that integer? Any other way to let the parent know how child finishes its job?
Also, in the function: $dead_and_gone = pcntl_waitpid(-1,$status,WNOHANG);
what is in the variable $status?
Thanks!
peter
I haven’t needed to get the process exit code for anything yet, but pcntl_waitpid says this:
You can pass $status into pcntl_wexitstatus to get the process code to get the exit code, if pcntl_wifexited returns TRUE.
The PHP docs will give you more details, examples and caveats http://www.php.net/manual/en/function.pcntl-wifexited.php
Pingback: Adding Pcntl Support to a Shared Host | Stuporglue.org
Pingback: Creating a daemon « Abner’s Postgraduate Days
Hi Stuporglue,
Great write up. I have been using php daemons for some time now but this article has given me ideas about something I was scratching my head over. Thanks.
@Jacob
Referring to : “If you daemonize, close your terminal, and then your daemon tries to print, you’re going to have problems. Your daemon will still try to print to STDOUT, which will now be closed. This will likely cause it to fail in an unpleasant manner.”
When you write daemons, you really should not be putting print or echo statements in your code at all. You should write to a log file if you need output using error_log.
Brian
Great article. I’ve just looked at your code, but I will certainly test this later.
When it comes to logging, check this out: http://logging.apache.org/log4php/
I am using log4net quite a lot in .Net applications, and if the php-version is as good as the log4net, then you have a good logging library to use.
Hi Stuporglue,
I was wondering, in the file “worker.php”, how would I access the arguments I want to pass through from the controller?
Hi John,
Programs launched with pcntl_exec($program,$arguments); are run as command line scripts, so you’ll access the arguments via $argv.
You can launch non-PHP programs too.
If your script is PHP you’ll either need to set the shebang to use PHP, or set $program to /usr/bin/php and have the actual script be one of the arguments.
1. Don’t need “php -q” for PHP CLI.
2. You really want to fork twice.
$pid = pcntl_fork();
if($pid){
// Only the parent will know the PID. Kids aren't self-aware
// Parent says goodbye!
print "\tParent : " . getmypid() . " exiting\n";
exit();
}
// Dissociate from controlling terminal, become session leader
if (posix_setsid() === -1) {
die("Error!\n");
}
usleep(100000);
// Fork again as session leader
$pid = pcntl_fork();
3. To deal with the “stray I/O”, once you’ve closed the standard file descriptors, open them again, pointed to /dev/null
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$stdin = fopen('/dev/null', 'r'); // set fd/0
$stdout = fopen('/dev/null', 'w'); // set fd/1
$stderr = fopen('php://stdout', 'w'); // hack to duplicate fd/1 to 2
Thanks for such useful article. I have such question: How can I send the data to the daemon. Not signal, but some array of data? In your example, you have empty $arguments array. How will you populate it, in case needed?
Thanks again!
$arguments is in there because pcntl_exec can take an array of arguments for the program which is executed.
There are several ways you might communicate with the daemon, it all depends what you’re looking for.
Personally, I have a database set up which accepts job requests from users. The daemon queries the database every time through the while(TRUE) loop to check for more jobs (with a sleep(3) at the end of the while loop so it’s not always running).
You could also write to a config file (or somewhere else), then have a signal handler and send the daemon a signal to read or re-read those files.
You could also look into using a named pipe which would let other processes write to the pipe which the daemon could read from on every loop.
Sockets might also do the job, if that’s what needed.