Multiprocess Demons in PHP
Why might you need to write daemons in PHP?
If you have a question about some unfamiliar function - don't be upset! They are all documented in PHP Manual. It is unlikely that I will be able to talk about them in more detail and more interesting.
How to make two from one process? Windows programmers (including me) are more familiar with the system when we write a function that will be
So. There is such a function
After
The trick is that
By the way, it
To demonize a script, you need to untie it from the console and put it into an endless loop. Let's see how this is done.
After such actions, we are left with a demon - a program without a console. So that she does not complete her execution immediately, we put her into an endless loop (well, almost):
At the moment, our demon is single-process. For a number of obvious reasons, this may not be enough. Consider creating child processes.
The next most important task is to provide signal processing. Now our demon knows nothing about the outside world, and you can kill him only by completing the process through
There are a bunch of interesting signals that can be processed, but we will focus on the
That's all. We intercept the signal - set the flag in the script - use this flag so as not to start new threads and complete the main loop.
And the final touch. It is necessary that the daemon does not start twice. Usually for these purposes, so-called .pid files - the file in which the pid of this particular daemon is written, if it is running.
And after demonization, you need to write the current
That's all there is to know for writing daemons in PHP. I did not talk about shared access to resources, because this problem is wider than writing demons.
Good luck
Syntax highlighted article on my blog .
- Performing time-consuming background tasks;
- execution of tasks that last longer than the timeout for an HTTP request (30 seconds);
- performing tasks at a higher level of access than the server process (read - under root).
The basics
- PID - process identifier. Unique for the current moment positive number.
- pcntl is a PHP extension for working with child processes. We smoke a manual.
- posix is a PHP extension for working with the functions of the POSIX standard. We smoke a manual.
If you have a question about some unfamiliar function - don't be upset! They are all documented in PHP Manual. It is unlikely that I will be able to talk about them in more detail and more interesting.
Forking (we produce processes)
How to make two from one process? Windows programmers (including me) are more familiar with the system when we write a function that will be
main()
for a child thread. In * nix, this is not so, because I will talk a little about this multiprocessing system. * nixoids can safely skip this part if they already know everything. So. There is such a function
pcntl_fork
. Oddly enough, she takes no arguments. What to do? After
pcntl_fork
the script, schizophrenia begins: the code seems to be the same, but it is executed by two parallel processes. However, if you simply paste it into a script pcntl_fork
, you will not see anything visual, except that there are conflicts of access to resources. The trick is that
pcntl_fork
returns 0 to the child process and the PID of the child process to the parent. Here is a common usage pattern pcntl_fork
:$ pid = pcntl_fork (); if ($ pid == -1) { //error } elseif ($ pid) { // parent process gets here } else { // and here is the child process } // and both processes will get here
By the way, it
pcntl_fork
works only in CGI and CLI modes. From under the Apache - it is impossible. Is logical.Demonization
To demonize a script, you need to untie it from the console and put it into an endless loop. Let's see how this is done.
// create a child process $ child_pid = pcntl_fork (); if ($ child_pid) { // exit the parent console-bound process exit } // make the main process a child. // After that, he can also bear children. // The harsh life of these processes ... posix_setsid ();
After such actions, we are left with a demon - a program without a console. So that she does not complete her execution immediately, we put her into an endless loop (well, almost):
while (! $ stop_server) { // TODO: doing something }
Child processes
At the moment, our demon is single-process. For a number of obvious reasons, this may not be enough. Consider creating child processes.
$ child_processes = array (); while (! $ stop_server) { if (! $ stop_server and (count ($ child_processes) <MAX_CHILD_PROCESSES)) { // TODO: get the task // produce the child process $ pid = pcntl_fork (); if ($ pid == -1) { // TODO: error - could not create the process } elseif ($ pid) { // process created $ child_processes [$ pid] = true; } else { $ pid = getmypid (); // TODO: child process - here is the workload exit } } else { // so as not to idle the cycle sleep (SOME_DELAY); } // check if one of the children died while ($ signaled_pid = pcntl_waitpid (-1, $ status, WNOHANG)) { if ($ signaled_pid == -1) { // no children left $ child_processes = array (); break; } else { unset ($ child_processes [$ signaled_pid]); } } }
Signal processing
The next most important task is to provide signal processing. Now our demon knows nothing about the outside world, and you can kill him only by completing the process through
kill -SIGKILL
. This is bad. This is very bad - it SIGKILL
will interrupt the processes in the middle. In addition, information cannot be transmitted to him. There are a bunch of interesting signals that can be processed, but we will focus on the
SIGTERM
- signal of a correct shutdown.// Without this directive, PHP will not intercept signals declare (ticks = 1); // Handler function sigHandler ($ signo) { global $ stop_server; switch ($ signo) { case SIGTERM: { $ stop_server = true; break; } default: { // all other signals } } } // register the handler pcntl_signal (SIGTERM, "sig_handler");
That's all. We intercept the signal - set the flag in the script - use this flag so as not to start new threads and complete the main loop.
Maintaining the uniqueness of the demon
And the final touch. It is necessary that the daemon does not start twice. Usually for these purposes, so-called .pid files - the file in which the pid of this particular daemon is written, if it is running.
function isDaemonActive ($ pid_file) { if (is_file ($ pid_file)) { $ pid = file_get_contents ($ pid_file); // check for the presence of a process if (posix_kill ($ pid, 0)) { // daemon is already running return true; } else { // there is a pid file, but there is no process if (! unlink ($ pid_file)) { // I can’t destroy the pid file. error exit (-1); } } } return false; } if (isDaemonActive ('/ tmp / my_pid_file.pid')) { echo 'Daemon already active'; exit }
And after demonization, you need to write the current
PID
daemon to the pid file .file_put_contents ('/ tmp / my_pid_file.pid', getmypid ());
That's all there is to know for writing daemons in PHP. I did not talk about shared access to resources, because this problem is wider than writing demons.
Good luck
Syntax highlighted article on my blog .