PHP でデーモンを作ろうとして、少しハマった。
同じことで悩んでいる人がいるかもしれないので、簡単に記しておく。
作ろうとしたのは、デーモンの「親」が、多数の「子プロセス」を作って、同時並行処理する、というもの。
なにぶん PHP だから、ひょんなことで終了する可能性がある。
だから、子プロセスが終了したら、親は再び子プロセスを起動する。
万が一親プロセスが死んだら…これは、deamontools を使っているので気にしない。
親プロセスが子プロセスの終了を待つには、pcntl_wait する。
親プロセス自身が TERM や HUP シグナルを受け取ったら、子プロセスを全部安全に終了させてから親が終了しなくてはならない。
これには、pcntl_signal を使う。
pcntl_wait は、
この関数は、子プロセスが終了する・ カレントのプロセスを終了させるシグナルが送信される・シグナル処理関数を コールするシグナルが送信される のいずれかが発生するまでカレントのプロセスの実行を中断します。
…と、関数リファレンスに書いてある。
つまり、子プロセスが(1つでも)終了するか、親プロセスにシグナルが来るまでは pcntl_wait で止まっている、ということだ。
pcntl_signal は、シグナルを受け取ったときに処理する関数(コールバック関数)を登録する命令なのだが、
PHP 4.3.0 以降、PCNTL はシグナルハンドルコールバックの仕組みとして ticks を使用しており、これは以前の仕組みよりずっと高速です。
…と、関数リファレンスに書いてある。
ticks というのは、PHP の命令と命令の隙間だ。厳密に命令と対応しているわけではないが、ともかく「PHP 内部の処理が終わって、スクリプトの命令を読み込むとき」に ticks が発生する。
そして、PHP のバージョン 4.3.0 以降は、この ticks でシグナルを受け取る、というのだ。
…あれ?
pcntl_wait は、命令中でもシグナルを受け取る、といっている。
pcntl_signal は、シグナルは命令の間で受け取る、といっている。
この矛盾に気づくまで、思ったように動作しないでかなり悩んだ。
実際には、pcntl_wait 中は、シグナルを受け取ることができない。
だから、親の動作が「子プロセスの終了を待つだけ」であっても、pcntl_wait で停止してはいけない。
おそらく、4.3.0 以前は、pcntl_wait はシグナルを受け取っていたのだろう。
幸いなことに、pcntl_wait には「停止せず、子プロセスが終了していないなら 0 を返す」というオプションがある。
これを使って、シグナルを待ちながら子プロセスの終了も待つ、というメインループを作ったら、シグナルを受け取れるようになった。
さらに言うなら、ticks を使ってシグナルを受け取ろうとすると、どこで割り込みが発生するかわからず危険な上、命令ごとに割り込みを処理しようとするので遅いようだ。いいことがひとつもない。
ticks ごとの割り込みは declare 命令を使って制御するが、それ以外に pcntl_signal_dispatch を使うことで、シグナル処理の明示的なタイミングを指示することもできる。
こちらを使ってみたら、安全なタイミングで処理できるし、速度も速くなった。
…しかし、PHP って deamon 作るのに向いている言語ではない。
そんなことは最初からわかっているが、今回はどうしても PHP で書かれたライブラリを使用する必要があったのだ。
同じテーマの日記(最近の一覧)
関連ページ
別年同日の日記
申し訳ありませんが、現在意見投稿をできない状態にしています。 |