始める前に:phpDaemonとSystem_Daemonが何であるかを知っています。 私はこのテーマに関する記事を読んでいます。
それで、あなたはすでにあなたが悪魔を必要とすると決めたと仮定します。 彼は何ができるはずですか?
- コンソールから実行し、それを取り除く
- ログにすべての情報を書き込みます。コンソールには何も出力しません
- 子プロセスを生成および制御できる
- タスクを完了する
- 正しくシャットダウンする
コンソールから取り外します
// // pcntl_fork() : $child_pid = pcntl_fork(); if ($child_pid) { // , , exit(); } // . posix_setsid(); // ,
pcntl_fork()関数は子プロセスを作成し、その識別子を返します。 ただし、変数$ child_pidは子プロセスに入りません(より正確には、0になります)。したがって、親プロセスのみがテストに合格します。 それは終了し、子プロセスはコードの実行を継続します。
通常、デーモンはすでに作成されていますが、コンソールにはすべての情報(エラーを含む)が表示されます。 はい。実行後すぐに終了します。
出力を再定義する
$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');
ここで、標準出力ストリームを閉じて、それらをファイルに送信します。 念のため、STDINは/ dev / nullからの読み取り用に開かれています。 私たちの悪魔はコンソールから読み込まれません-それは解かれています。 これで、デーモンのすべての出力がファイルに記録されます。
行こう!
include 'DaemonClass.php'; $daemon = new DaemonClass(); $daemon->run();
結論を再定義した後、デーモンに割り当てられたタスクを実行できます。 DaemonClass.phpを作成し、デーモンの基本的な作業を行うクラスの作成を開始します。
Daemonclass.php
// PHP declare(ticks=1); class DaemonClass { // public $maxProcesses = 5; // TRUE, protected $stop_server = FALSE; // protected $currentJobs = array(); public function __construct() { echo "onstructed daemon controller".PHP_EOL; // SIGTERM SIGCHLD pcntl_signal(SIGTERM, array($this, "childSignalHandler")); pcntl_signal(SIGCHLD, array($this, "childSignalHandler")); } public function run() { echo "Running daemon controller".PHP_EOL; // $stop_server TRUE, while (!$this->stop_server) { // , while(count($this->currentJobs) >= $this->maxProcesses) { echo "Maximum children allowed, waiting...".PHP_EOL; sleep(1); } $this->launchJob(); } } }
シグナルSIGTERM(シャットダウン)およびSIGCHLD(子プロセスから)が必要です。 デーモンが終了しないように、無限ループを開始します。 別の子プロセスを作成できるかどうかを確認し、できない場合は待機します。
protected function launchJob() { // // pcntl_fork() // : $pid = pcntl_fork(); if ($pid == -1) { // error_log('Could not launch new job, exiting'); return FALSE; } elseif ($pid) { // $this->currentJobs[$pid] = TRUE; } else { // echo " ID ".getmypid().PHP_EOL; exit(); } return TRUE; }
エラーが発生した場合、 pcntl_fork()は-1を返します。$ pidは親プロセスで使用可能になり、この変数は子にはありません(より正確には、0になります)。
public function childSignalHandler($signo, $pid = null, $status = null) { switch($signo) { case SIGTERM: // $this->stop_server = true; break; case SIGCHLD: // if (!$pid) { $pid = pcntl_waitpid(-1, $status, WNOHANG); } // while ($pid > 0) { if ($pid && isset($this->currentJobs[$pid])) { // unset($this->currentJobs[$pid]); } $pid = pcntl_waitpid(-1, $status, WNOHANG); } break; default: // } }
SIGTERM-正しいシャットダウンのシグナル。 SIGCHLD-子プロセスの完了のシグナル。 子プロセスの最後に、実行中のプロセスのリストから子プロセスを削除します。 SIGTERMを受信したら、フラグを設定します。現在のタスクが完了すると、「無限ループ」が終了します。
デーモンの複数のコピーの起動を禁止することは残っています。 これについては、 この記事で詳しく説明しています。
ご清聴ありがとうございました。
UPD: Dlussky habrayuzerのコメントでは、PHP> = 5.3.0では、 declare(ticks = 1)ではなくpcntl_signal_dispatch()を使用する必要があることが示唆されました。