PHP proc_open是否会阻止Web请求? [英] Does PHP proc_open block the Web request?

查看:103
本文介绍了PHP proc_open是否会阻止Web请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

默认情况下,在Linux上,通过proc_open()创建进程是否会使PHP脚本直到生成的进程终止后才终止?我不想要它,我立即关闭了进程句柄.

By default, on Linux, does creating a process via proc_open() make the PHP script not terminate until the spawned process terminates? I don't want it to, and I close the process handle right away.

proc_open本身不会阻止,这很清楚.但是总体HTTP请求执行情况如何?

proc_open itself does not block, that's clear enough. But what about the overall HTTP request execution?

推荐答案

我周末有一些时间,所以我对* nix系统上的proc_open()进行了一些研究.

I had some time on the weekend, so I made a little research on proc_open() on *nix systems.

尽管proc_open()不会阻止PHP的脚本执行,即使您没有在后台运行Shell脚本,如果您自己不调用PHP的脚本,则在PHP的脚本完全执行后,PHP也会自动调用proc_close().因此,我们可以想象脚本的末尾总是有一行与proc_close()的行.

While proc_open() doesn't block PHP's script execution even if shell script is not run in background PHP automatically invokes proc_close() after PHP's script is completely executed if you don't invoke it yourself. So, we can imagine that we have always a line with proc_close() in the end of script.

问题在于不明显但符合逻辑的proc_close()行为.假设我们有一个像这样的脚本:

The problem lies with unobvious, but logical proc_close() behaviour. Let's imagine we have a script like:

$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()会在外壳程序脚本执行结束之前等待,但是我们的脚本很快终止了.之所以会发生这种情况,是因为我们关闭了管道(如果我们忘记了,PHP会默默地执行此操作),所以一旦该脚本尝试向不存在的管道中写入内容,它就会出错并终止.

Strange, proc_close() whould wait before shell script execution ended but our script was soon terminated. It hapens because we closed pipes(it seems that PHP does it silently if we forgot) so as soon as that script tries to write something to already non-existent pipe - it gets an error and terminates.

现在,让我们尝试不使用管道(当然,会有管道,但是它们将使用当前的tty而没有任何PHP链接):

Now, let's try without pipes (well, there will be, but they will use current tty without any link to PHP):

$proc = proc_open("top -b -n 10000", array(), $pipes);
proc_close($proc);

现在,我们的PHP脚本正在等待shell脚本结束.我们可以避免吗?幸运的是,PHP使用

Now, our PHP script is waiting for our shell script to end. Can we avoid it? Luckily PHP spawns shell scripts with

sh -c 'shell_script'

因此,我们可以终止sh进程并使脚本运行:

so, we can just kill out sh process and leave our script running:

$proc = proc_open("top -b -n 10000", array(), $pipes);
$proc_status=proc_get_status($proc);
exec('kill -9 '.$proc_status['pid']);
proc_close($proc);

当然,我们可以在后台运行该过程,例如:

Of cource we could just have run the process in background like:

$proc = proc_open("top -b -n 10000 &", array(), $pipes);
proc_close($proc);

并没有任何问题,但是此功能将我们引到最复杂的问题:我们可以使用proc_open()运行进程以读取一些输出,然后将进程强制为后台吗?好吧,在某种程度上-是的.

and not have any problems, but this feature leads us to the most complex question: can we run a process with proc_open() read some output and then force the process to background? Well, in a way - yes.

这里的主要问题是管道:我们无法关闭它们,否则我们的进程将死,但是我们需要它们从该进程中读取一些有用的数据.事实证明,我们可以在这里使用魔术-gdb.

Main problem here is pipes: we can't close them or our process will die, but we need them to read some usefull data from that process. It turns out that we can use a magic trick here - gdb.

首先,在以下位置创建文件(在我的示例中为/usr/share/gdb_null_descr):

First, create a file somewhere(/usr/share/gdb_null_descr in my example) with following contents:

p dup2(open("/dev/null",0),1)
p dup2(open("/dev/null",0),2)

它将告诉gdb将描述符1和2(好吧,它们通常是stdout和stderr)更改为新的文件处理程序(在此示例中为/dev/null,但是您可以更改它).

It will tell gdb to change descriptors 1 and 2(well, they are usually stdout and stderr) to new file handlers (/dev/null in this example, but you can change it).

现在,最后一件事:确保gdb可以连接到其他正在运行的进程-在某些系统上是默认设置,但是例如在ubuntu 10.10上,如果您将/proc/sys/kernel/yama/ptrace_scope设置为0,不要以root身份运行它.

Now, last thing: make sure gdb can connect to other running processes - it is default on some systems, but for example on ubuntu 10.10 you have to set /proc/sys/kernel/yama/ptrace_scope to 0 if you are don't run it as 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);

edit:我忘了提到PHP不能立即运行您的Shell脚本,因此您必须稍等一会儿再执行其他Shell命令,但是通常它足够快(或者PHP足够慢),我懒惰地将检查添加到我的示例中.

edit: I forgot to mention that PHP doesn't run your shell script instantly, so you have to wait a bit before executing other shell commands, but usually it is fast enough(or PHP is slow enough) and I'm to lazy to add that checks to my examples.

这篇关于PHP proc_open是否会阻止Web请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆