PHP:是否需要关闭STDIN才能读取STDOUT? [英] PHP: Need to close STDIN in order to read STDOUT?

查看:57
本文介绍了PHP:是否需要关闭STDIN才能读取STDOUT?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近尝试使用PHP函数proc_open与Ubuntu网络服务器[1]上的二进制文件进行通信.我可以建立连接并定义管道STDIN,STDOUT和STDERR.很好.

I recently tried to communicate with a binary on my Ubuntu webserver [1] using the PHP function proc_open. I can establish a connection and define the pipes STDIN, STDOUT, and STDERR. Nice.

现在我要讨论的Bimary是一个交互式计算机代数软件-因此,我想在第一个命令后保持STDOUT和STDIN都处于活动状态,以便以后我仍可以以交互方式使用该应用程序几行(直接用户从Web前端输入的内容.

Now the bimary I am talking to is an interactive computer algebra software - therefore I would like to keep both STDOUT and STDIN alive after the first command such that I can still use the application a few lines later in an interactive manner (direct user inputs from a web-frontend).

但是,事实证明,读取二进制文件的STDOUT的PHP函数(stream_get_contents或fgets)需要一个封闭的STDIN才能起作用.否则程序将死锁.

However, as it turns out, the PHP functions to read the STDOUT of the binary (either stream_get_contents or fgets) need a closed STDIN before they can work. Otherwise the program deadlocks.

这是一个严重的缺陷,因为我不能在关闭它之后立即重新打开它.所以我的问题是:如果我的STDIN仍然存在,如果我想读取STDOUT,为什么我的脚本会死锁?

This is a severe drawback since I can not just reopen the closed STDIN after closing it. So my question is: why does my script deadlock if I want to read the STDOUT when my STDIN is still alive?

谢谢詹斯

[1] proc_open返回false,但不写入错误文件-权限问题?

我的来源:

$descriptorspec = array(
    0 => array("pipe","r"),
    1 => array("pipe","w"),
    2 => array("file","./error.log","a")
) ;

// define current working directory where files would be stored
$cwd = './' ;

// open reduce
$process = proc_open('./reduce/reduce', $descriptorspec, $pipes, $cwd) ;

if (is_resource($process)) {

    // some valid Reduce commands
    fwrite($pipes[0], 'load excalc; operator x; x(0) := t; x(1) := r;');

    // if the following line is removed, the script deadlocks
    fclose($pipes[0]);

    echo "output: " . stream_get_contents($pipes[1]);

    // close pipes & close process
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);

}

此代码类型有效.之所以这样,是因为它使用usleeps等待未阻塞的STDOUT充满数据.我该如何更优雅地做到这一点?

This code kind of works. Kind of because it uses usleeps to wait for the non-blocked STDOUT to be filled with data. How do I do that more elegantly?

@ Elias:通过轮询$ status ['running']条目,您只能确定整个进程是否仍在运行,而不能确定该进程是否忙碌或空闲……这就是为什么我必须包括这些usleeps

@ Elias: By polling the $status['running'] entry you can only determine if the overall process is still running, but not if the process is busy or idling... That is why I have to include these usleeps.

define('TIMEOUT_IN_MS', '100');
define('TIMEOUT_STEPS', '100');

function getOutput ($pipes) {
    $result = "";
    $stage = 0;
    $buffer = 0;
    do {
        $char = fgets($pipes[1], 4096);
        if ($char != null) {
            $buffer = 0;
            $stage = 1;
            $result .= $char;
        } else if ($stage == "1") {
            usleep(TIMEOUT_IN_MS/TIMEOUT_STEPS);
            $buffer++;
            if ($buffer > TIMEOUT_STEPS) {
                $stage++;
            }
        }
    } while ($stage < 2);
    return $result;
}

$descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w") ) ;

// define current working directory where files would be stored
$cwd = './' ;

// open reduce
$process = proc_open('./reduce/reduce', $descriptorspec, $pipes, $cwd);

if (is_resource($process)) {

    stream_set_blocking($pipes[1], 0);

    echo "startup output:<br><pre>" . getOutput($pipes) . "</pre>";

    fwrite($pipes[0], 'on output; load excalc; operator x; x(0) := t; x(1) := r;' . PHP_EOL);
    echo "output 1:<br><pre>" . getOutput($pipes) . "</pre>";

    fwrite($pipes[0], 'coframe o(t) = sqrt(1-2m/r) * d t, o(r) = 1/sqrt(1-2m/r) * d r with metric g = -o(t)*o(t) + o(r)*o(r); displayframe;' . PHP_EOL);
    echo "output 2:<br><pre>" . getOutput($pipes) . "</pre>";

    // close pipes & close process
    fclose($pipes[0]);
    fclose($pipes[1]);
    fclose($pipes[2]);
    proc_close($process);

}

推荐答案

这使我想起

This reminds me of a script I wrote a while back. While it might serve as inspiration to you (or others), it doesn't do what you need. What it does contain is an example of how you can read the output of a stream, without having to close any of the streams.
Perhaps you can apply the same logic to your situation:

$allInput = array(
    'load excalc; operator x; x(0) := t; x(1) := r;'
);//array with strings to pass to proc
if (is_resource($process))
{
    $output = '';
    $input = array_shift($allInput);
    do
    {
        usleep(200);//make sure the running process is ready
        fwrite(
            $pipes,
            $input.PHP_EOL,//add EOL
            strlen($input)+1
        );
        fflush($pipes[0]);//flush buffered data, write to stream
        usleep(200);
        $status = proc_get_status($process);
        while($out = fread($pipes[1], 1024) && !feof($pipes[1])) 
            $output .= $out;
    } while($status['running'] && $input = array_shift($allInput));
    //proc_close & fclose calls here
}

现在,由于我不知道您要尝试执行的操作是什么,因此需要对这段代码进行一些调整.例如,您可能发现自己不得不将STDIN和STDOUT管道设置为非阻塞.
只需在调用 proc_open 之后立即添加此代码即可,这很简单:

Now, seeing as I don't know what it is exactly you are trying to do, this code will need to be tweaked quite a bit. You may, for example, find yourself having to set the STDIN and STDOUT pipes as non-blocking.
It's a simple matter of adding this, right after calling proc_open, though:

 stream_set_blocking($pipes[0], 0);
 stream_set_blocking($pipes[1], 0);

玩耍,玩得开心,也许让我知道这个答案是否有任何帮助......

Play around, have fun, and perhaps let me know if this answer was helpful in any way...

这篇关于PHP:是否需要关闭STDIN才能读取STDOUT?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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