PHPデーモン

初心者のエクソシストへのメモ。



始める前に: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()を使用する必要があることが示唆されました。



All Articles