php でプロセスを起動するときに、パイプを作ってそれぞれの実行する。
php で pipeを作る例
<?php $p1_file_descriptor = [['pipe','r'],['pipe','w'],['pipe','w']]; $p1 = proc_open(['ls','/etc'],$p1_file_descriptor,$p1_pipes); fclose($p1_pipes[0]); $p2_file_descriptor = [$p1_pipes[1],['pipe','w'],['pipe','w']]; $p2 = proc_open(['grep','su'],$p2_file_descriptor,$p2_pipes); while(proc_get_status($p1)["running"]){ usleep(100); } while(proc_get_status($p2)["running"]){ usleep(100); } // $str = fread($p2_pipes[1],1024); var_dump($str);
ポイント
- proc_open は 引数に file discriptor をリンクで渡す
- ['pipe', 'r'] のように渡せば、パイプが作成される。
- linux のシステムコールでは dup/ pipe / fork / execv に相当する.
- proc_open はシステムコールを隠蔽してくれてる
- fclose(p1_pipe[0]) でp1 のstdin を閉じないとread がブロックされて起動しない
- p1 の stdout と p2 の stdin をつなぐ
パイプだけを作りたい
proc_openを使わずに、pipe して fork して exec したいときは、自分で作ることも不可能ではない
<?php $pipe = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0); switch (pcntl_fork()) { case -1: exit(255); case 0: // child fclose($pipe[1]); pcntl_exec("/path/to/cmd", ["-i", (int) $sp[0]]); default: // parent fclose($pipe[0]); do_as_parent(); } ?>
パイプだけを作ってコマンドに流し込みたい場合は、stream_socket_pair関数を用いて、UNIXドメインソケットを開けてあげる。stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, 0);
これで、パイプを開いてコマンドのSTDINへ流し込める。
何バイトを流し込んで、どれくらいの速度(Mbps)であと、どれくらいかを計測したいときに使えると思います。
大きいファイルを処理するときに使う。
ffmpeg や convert など、かなり大きなファイルを扱う場合は、STDIN/STDOUTを取り扱って、進捗を出すと便利なので、pv なども使えるし、コマンドのコマンドが書き出す進捗もリアルタイムでモニタリングできる。
そもそも、phpでそんなことをするべきではないかもしれないけど、扱えると何かと便利。
プロセス管理をパッケージにまかせていると、変なところでタイムアウトして不完全で止まったり、出力が詰まったりで面倒なので直接プロセスを取り扱ったほうが便利なことが多い。