PHP
Raiting:
17

Daemons in PHP


Here is a memo for novice exorcist:)

Before I begin, I know what are phpDaemon and System_Daemon. I read some articles about this subject.

So, let's assume that you've already decided that you need the daemon. What should daemon be able to do?

• It should run from the console and unbind from it.
• It should write all information to the logs, nothing output to the console.
• It should be able to create the child processes and monitor them.
• It should perform an assigned task.
• It should correctly complete a job.

Unbinding from the console

// Create the child process
// All the code after pcntl_fork () will be performed by two processes: parent and child
$child_pid = pcntl_fork();
if ($child_pid) {
// Exit from the parent process that is bound to the console
exit();
}
// Make the child as the main process.
posix_setsid();

// Further code will be executed only by the child, which is already unbound from the console.

Function pcntl_fork() creates a child process and returns its identifier. However, the variable $ child_pid does not get to the child process (rather it will be equal to 0), respectively, only the parent process will be tested. It will be finished, and the child process will continue to implement the code.

In general, the daemon was already created, but all the information (including errors) it will still be output to the console. It will be completed immediately after execution.

Redefining the output

$baseDir = dirname(__FILE__);
ini_set('error_log',$baseDir.'/error.log');
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen($baseDir.'/application.log', 'ab');
$STDERR = fopen($baseDir.'/daemon.log', 'ab');

Here we close the standard output streams and send them to a file. Just in case STDIN is opened to read from /dev/null, because our daemon will not read from the console. Now all output will be logged in the daemon files.

Let's go further!

include 'DaemonClass.php';
$daemon = new DaemonClass();
$daemon->run();

Once we have redefined the output, the daemon can perform the assigned task. Let us create DaemonClass.php and start writing a class that will do the main work of this daemon.

DaemonClass.php

// Without this directive, PHP will not intercept the signals
declare(ticks=1);

class DaemonClass {
// Maximum number of children
processes $maxProcesses = 5;
// When it will be set to TRUE, the daemon will complete the job
protected $stop_server = FALSE;
// Here we will keep running child processes
protected $currentJobs = array();

public function __construct() {
echo "Сonstructed daemon controller".PHP_EOL;
// Wait for the signals SIGTERM and SIGCHLD
pcntl_signal(SIGTERM, array($this, "childSignalHandler"));
pcntl_signal(SIGCHLD, array($this, "childSignalHandler"));
}

public function run() {
echo "Running daemon controller".PHP_EOL;

// While $stop_server will not be set to TRUE, running an endless loop
while (!$this->stop_server) {
// If the maximum number of child processes were already run, we wait for their completion
while(count($this->currentJobs) >= $this->maxProcesses) {
echo "Maximum children allowed, waiting...".PHP_EOL;
sleep(1);
}

$this->launchJob();
}
}
}

We wait for the SIGTERM signals (completion of work) and SIGCHLD (from the child processes). Let us run the infinite loop in order to keep the daemon running. Then we check whether a child process can still be created, and we wait if we cannot
.
protected function launchJob() {
// Create the child process
// all the code after pcntl_fork () will be executed by
// two processes: parent and child
$pid = pcntl_fork();
if ($pid == -1) {
// Could not create a child process
error_log('Could not launch new job, exiting');
return FALSE;
}
elseif ($pid) {
// This code will be executed by the parent process
$this->currentJobs[$pid] = TRUE;
}
else {
// And this code will be executed by the child process
echo " Process with ID ".getmypid().PHP_EOL;
exit();
}
return TRUE;
}

pcntl_fork() returns -1 if an error occurs, $pid will be available in the parent process, and in the child process of this variable it will be equal to 0.


public function childSignalHandler($signo, $pid = null, $status = null) {
switch($signo) {
case SIGTERM:
// When the completion of work signal is received we set flag
$this->stop_server = true;
break;
case SIGCHLD:
// When the signal is received from the child process
if (!$pid) {
$pid = pcntl_waitpid(-1, $status, WNOHANG);
}
// As long as there are the completed child processes
while ($ pid> 0) {
if ($pid && isset($this->currentJobs[$pid])) {
// Remove the child processes from the list
unset($this->currentJobs[$pid]);
}
$pid = pcntl_waitpid(-1, $status, WNOHANG);
}
break;
default:
// All other signals
}
}

SIGTERM is a signal of correct termination of work. SIGCHLD is a signal of termination of the child process. When the child process is completed, we remove it from the list of running processes. A flag is set when we receive SIGTERM, and our infinite loop ends, when the current task will be executed.

Here is a link to daemon website as a bonus.

Thank you for your attention!
Tags: daemon, Php
ZimerMan 20 february 2012, 16:34
Vote for this post
Bring it to the Main Page
 

Comments

Leave a Reply

B
I
U
S
Help
Avaible tags
  • <b>...</b>highlighting important text on the page in bold
  • <i>..</i>highlighting important text on the page in italic
  • <u>...</u>allocated with tag <u> text shownas underlined
  • <s>...</s>allocated with tag <s> text shown as strikethrough
  • <sup>...</sup>, <sub>...</sub>text in the tag <sup> appears as a superscript, <sub> - subscript
  • <blockquote>...</blockquote>For  highlight citation, use the tag <blockquote>
  • <code lang="lang">...</code>highlighting the program code (supported by bash, cpp, cs, css, xml, html, java, javascript, lisp, lua, php, perl, python, ruby, sql, scala, text)
  • <a href="http://...">...</a>link, specify the desired Internet address in the href attribute
  • <img src="http://..." alt="text" />specify the full path of image in the src attribute