鱒身(Masu_mi)のブログ

知った事をメモする場所。

PHPでシグナルを扱う

PHP でシグナルを扱うには プロセス制御 を利用する。 その際に必要な ticks 関数の設定についてメモする。

その前に

pcntl が使えるか確認する。以下で実行可能か確認しておく。

$ php -i | awk /[p]cntl\ support/
pcntl support => enabled

OSXで標準で入っているのはコンパイルオプション –enable-pcntl が有効になってなかった。 公式に従ってインストールしてしまうphpenv で有効なバージョンを入れる。 phpenv でインストールした。

# phpenvでbuildする時のオプションの確認
$ awk /pcntl/ ~/.phpenv/plugins/php-build/share/php-build/default_configure_options
--enable-pcntl
$ phpenv intall 5.5.5

declare(ticks = 1)を有効にする

シグナルハンドラの登録は pcntl-signal を使う。 で 4.3.0以降は declare(ticks = 1); をコードブロックに記述しないと動かない。

これはシグナルのディスパッチを ticks 関数の pcntl_signal_dispatch が実行される事でシグナルハンドラが叩かれるのだけど、 ticks の仕組みは効率が悪いって事で、4.3.0以降では declare で明記が必要だから。 なのでこんな感じのコードにすると動く。

<?php
// test.php
declare(ticks = 1);
pcntl_signal(SIGHUP, handler);
function handler($signo) {
  echo 'in '. __FUNCTION__. PHP_EOL;
  exit(1);
}
while (true) {
  echo 'in loop. '. __FILE__. PHP_EOL;
  sleep(1);
}

動かした感じ。

$ php -d open_basedir=/ test.php
in loop test.php
in loop test.php
in loop test.php
...
# 別ターミナルで
$ kill -s 1 $PID
# 動いてた方が応えている
in handler

declareの位置を注意する

グローバル環境で declare 設定されると呼び出されたコードブロックの中でも設定は有効。 だけど呼び出された側のグローバル環境で設定しても呼び出し元には影響しない。 なので下のコードはSIGHUPを受け取ってくれない。

<?php
// test.php
pcntl_signal(SIGHUP, handler);
function handler($signo) {
  echo 'in '. __FUNCTION__. PHP_EOL;
  exit(1);
}
require 'required.php';
while (true) {
  echo 'in loop. '. __FILE__. PHP_EOL;
  sleep(1);
}
<?php
// required.php
declare(ticks = 1);