それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

php の proc_openで pipe(パイプ)を作る例

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でそんなことをするべきではないかもしれないけど、扱えると何かと便利。

プロセス管理をパッケージにまかせていると、変なところでタイムアウトして不完全で止まったり、出力が詰まったりで面倒なので直接プロセスを取り扱ったほうが便利なことが多い。

参考資料