默认情况下,在Linux上,是否通过proc_open()创建进程会使PHP脚本在生成的进程终止之前不会终止?我不想要它,我立即关闭了进程句柄。
proc_open本身并没有阻止,这很清楚。但是整个HTTP请求执行呢?
答案 0 :(得分:14)
周末我有时间,所以我在* nix系统上对proc_open()进行了一些研究。
虽然proc_open()不会阻止PHP的脚本执行,即使shell脚本没有在后台运行,如果你自己不调用PHP的脚本,PHP会自动调用proc_close()。所以,我们可以想象我们在脚本的末尾总是有一行proc_close()。
问题在于不明显但逻辑上的proc_close()行为。让我们假设我们有一个类似的脚本:
$proc = proc_open('top -b -n 10000',
array(
array('pipe', 'r'),
array('pipe', 'w')),
$pipes);
//Process some data outputted by our script, but not all data
echo fread($pipes[1],100);
//Don't wait till scipt execution ended - exit
//close pipes
array_map('fclose',$pipes);
//close process
proc_close($proc);
奇怪的是,proc_close()会在shell脚本执行结束之前等待,但我们的脚本很快就会被终止。它很好,因为我们关闭了管道(如果我们忘记了,似乎PHP会默默地执行它)所以只要该脚本试图向已经不存在的管道写入内容 - 它就会出错并终止。
现在,让我们试试没有管道(好吧,会有,但他们将使用当前的tty而没有任何PHP链接):
$proc = proc_open("top -b -n 10000", array(), $pipes);
proc_close($proc);
现在,我们的PHP脚本正在等待我们的shell脚本结束。我们能避免吗?幸运的是,PHP使用
生成shell脚本sh -c 'shell_script'
所以,我们可以杀掉sh进程并让脚本运行:
$proc = proc_open("top -b -n 10000", array(), $pipes);
$proc_status=proc_get_status($proc);
exec('kill -9 '.$proc_status['pid']);
proc_close($proc);
对于cource,我们可以在后台运行该过程,如:
$proc = proc_open("top -b -n 10000 &", array(), $pipes);
proc_close($proc);
并没有任何问题,但是这个功能引出了我们最复杂的问题:我们可以运行proc_open()读取一些输出的进程然后强制进程到后台吗?好吧,在某种程度上 - 是的。
这里的主要问题是管道:我们无法关闭它们或者我们的流程会死亡,但我们需要它们从该流程中读取一些有用的数据。事实证明,我们可以在这里使用魔术 - gdb。
首先,在某处创建一个文件(在我的示例中为/ usr / share / gdb_null_descr),其中包含以下内容:
p dup2(open("/dev/null",0),1)
p dup2(open("/dev/null",0),2)
它将告诉gdb将描述符1和2(好吧,它们通常是stdout和stderr)更改为新文件处理程序(在此示例中为/ dev / null,但您可以更改它)。
现在,最后一件事:确保gdb可以连接到其他正在运行的进程 - 它在某些系统上是默认的,但是例如在ubuntu 10.10上你必须将/ proc / sys / kernel / yama / ptrace_scope设置为0如果你是不要以root身份运行它。
享受:
$proc = proc_open('top -b -n 10000',
array(
array('pipe', 'r'),
array('pipe', 'w'),
array('pipe', 'w')),
$pipes);
//Process some data outputted by our script, but not all data
echo fread($pipes[1],100);
$proc_status=proc_get_status($proc);
//Find real pid of our process(we need to go down one step in process tree)
$pid=trim(exec('ps h -o pid --ppid '.$proc_status['pid']));
//Kill parent sh process
exec('kill -s 9 '.$proc_status['pid']);
//Change stdin/stdout handlers in our process
exec('gdb -p '.$pid.' --batch -x /usr/share/gdb_null_descr');
array_map('fclose',$pipes);
proc_close($proc);
编辑:我忘了提到PHP没有立即运行你的shell脚本,所以在执行其他shell命令之前你必须等一下,但通常它足够快(或者PHP足够慢)而且我是懒得将检查添加到我的例子中。
答案 1 :(得分:1)
我遇到了类似的问题并编写了一个小脚本来处理它:
https://github.com/peeter-tomberg/php-shell-executer
我所做的是介绍该过程,并且仍然可以访问后台进程的结果(stderr和stdout)。