単純なフォーク
<?php $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent } else { // we are the child sleep(10); var_dump("We are the child:", $pid); exit; }
実行結果
pcntl_wait($status); //Protect against Zombie children var_dump("END :", $pid); \-+= 48201 takuya -bash \-+= 49054 takuya php fork.php \--- 49055 takuya php fork.php
49054 から 49055 が生えた
2個フォークした場合
<?php $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent } else { // we are the child sleep(10); var_dump("We are the child:", $pid); exit; } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent } else { // we are the child sleep(10); var_dump("We are the child1"); exit; } pcntl_wait($status); //Protect against Zombie children var_dump("END :", $pid);
実行中の状態
\-+= 48802 takuya php fork.php |--- 48803 takuya php fork.php \--- 48804 takuya php fork.php
48802 から 48803, 48804 が生えた
フォークからさらにフォークした場合。
子プロセスから更に子プロセス、つまり孫プロセスを起動したら
<?php $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent } else { // we are the child sleep(10); var_dump("We are the child:", $pid); exit; } $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent } else { // we are the child sleep(10); var_dump("We are the child1"); exit; } pcntl_wait($status); //Protect against Zombie children var_dump("END :", $pid);
この場合、子プロセスが孫より先に死ぬので孫が迷子になってしまう。孤児としてrootプロセスに引き取られる。
なのでちゃんと管理をしなくてはいけない。
fork 先でしっかり終了させる。つまり単純な作業に限定するとか、終了待ちをきちんとする。など
また、子プロセスが生成した子プロセスを見るには再帰して ps -o pid,ppid ax
で 子プロセスpidを ppid にもつ孫を探すことになるかもしれない。
fork の注意点
fork を使う前に POSIX fork を知る必要がある。
fork のタイミングでメモリがコピーされますが、リソース(ファイルディスクリプタ)はコピーされると面倒が起きます。MySQLやHTTPなどのコネクションもfd(ファイルディスクリプタ)になってたりするので、fork 先でコネクションしないとエラーになることがある。
fork のコスト
fork するとメモリがコピーされるので、若干負荷が大きい操作になる。そこから先は別プロセスになるので、fork後のメモリの変更(変数の値変更)は別になる。
平行世界が作成されるイメージ
fork の利点
php でも時間がかかる処理は fork 先に任せて、メインは違うことに集中するなどと出来る。
コストは高いが、ジョブを待つだけの メインがあってジョブが来たら fork して仕事をやらせるみたいなことも出来る。
シグナルハンドラ
fork した両方のプロセスでシグナルをハンドリングするには、pcntl_signal_dispatch
を入れてあげないといけないみたい
pcntl_signal_dispatch();
の代わりに declare(ticks=1)
を入れることで解決はしますが、tick を小さくするとパフォーマンスに影響します。
Ctrl+C(SIGINT)を押しても即時反応しない場合とかもdispatchまでいったんキューに入るからだと思います。
pcntl_signal_dispatch はその名前の通り、シグナルキューをそれぞれのハンドラに振り分けるものですね。
<?php // signal handler function function sig_handler($signo) { echo "SIGNAL:" . $signo."\n"; switch ($signo) { case SIGTERM: echo 'cactch SIGTERM'."\n"; // handle shutdown tasks exit; break; case SIGINT: echo 'cactch SIGINT'."\n"; exit; case SIGHUP: echo 'cactch SIGHUO'."\n"; break; case SIGUSR1: echo "Caught SIGUSR1...\n"; echo 'pid:'.posix_getpid(); echo ' ppid:'.posix_getppid(); break; default: echo 'default'; } } pcntl_signal(SIGTERM, "sig_handler"); pcntl_signal(SIGINT, "sig_handler"); pcntl_signal(SIGHUP, "sig_handler"); pcntl_signal(SIGUSR1, "sig_handler"); pcntl_signal_dispatch(); while (1){ $a = 0; echo 'pid:'.posix_getpid()."\n"; $pid = pcntl_fork(); if ($pid===0) { // we are the child pcntl_signal_dispatch(); sleep(5); var_dump("We are the child:", $pid); exit; }else{ pcntl_signal_dispatch(); } pcntl_wait($status); //Protect against Zombie children var_dump("waiting is end :", $pid); }
linux の プロセス管理についてのおすすめ本
Linux のexecコールやfork とプロセス管理がしっかり分かる本だった。
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2005/07/27
- メディア: 単行本
- 購入: 35人 クリック: 450回
- この商品を含むブログ (150件) を見る
参考資料
pcntl - What's the relation between declare(ticks) and a signal handler in php - Stack Overflow