mod_php(Apache/PHP)で、ページの表示時にバックグラウンドで別プロセスを起動して並列に処理したい場合、次のように書くことが出来ます。
<?php system('/bin/ping 127.0.0.1 1>/dev/null 2>&1 &');
ただし、この方法だと Apache の子プロセスの停止や再起動時に、起動したプロセス(上の例なら ping コマンド)に HUP や TERM や USR1 が投げられるため、起動したプロセスの方でそのシグナルを処理していなければ強制終了されます。
そこで、次のようにデーモン化のスクリプトを挟んでプロセスを親から切り離し、Apache の停止や再起動とは無関係に処理を続行できるようにしました。
daemon.php
<?php $pid = pcntl_fork(); if($pid == 0) { posix_setsid(); $pid = pcntl_fork(); if($pid == 0) { pcntl_exec('/bin/ping', array('127.0.0.1')); } }
Webページ側の PHP では次のように呼び出します。
<?php system('/usr/bin/php /path/to/daemon.php 1>/dev/null 2>&1`');
・・・と思ったのですが、apache の stop → start で上手く行かなくなりました。。。
上の方法で ping コマンドをバックグラウンドで実行させたあとに apachectl stop → apachectl start すると次の通りエラーになりました。
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:443 no listening sockets available, shutting down
lsof -i:443 で見てみると・・・
ping 14860 apache 3u IPv4 639201 0t0 TCP *:https (LISTEN)
バックグラウンドで実行中の ping がリッスンソケットを開きっぱなしにしているようです。
Linux の fork はファイルディスクリプタ(ソケット含む)を複製するため、fork 後に子プロセスでそれらを閉じなければ、親プロセスが終了した後も意図せずファイルディスクリプタが残ったままになります。標準出力などは system() 実行時のリダイレクションでなんとかなっているようですが、Apache のリッスンソケットは PHP 側ではどうしようもありませんね・・・
Apache のスーパープロセスが子プロセスを fork した後に子プロセスに複製されたリッスンソケットは全部閉じとけよ! っと思いましたが、prefork なので accept は Apache 子プロセス側で行なっているため閉じれないでしょね。