php – 使用proc_open时从STDIN管道读取

前端之家收集整理的这篇文章主要介绍了php – 使用proc_open时从STDIN管道读取前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试建立一个人们可以在线编译和运行代码的网站,因此我们需要找到一种用户发送指令的交互方式.

实际上,首先想到的是exec()或system(),但是当用户想要输入时,这种方式将无法工作.所以我们必须使用proc_open().

例如,以下代码

int main()
{
    int a;
    printf("please input a integer\n");
    scanf("%d",&a);
    printf("Hello World %d!\n",a);
    return 0;
}

当我使用proc_open()时,就像这样

$descriptorspec = array(      
0 => array( 'pipe','r' ),1 => array( 'pipe','w' ),2 => array( 'file','errors','w' ) 
);  
$run_string = "cd ".$addr_base."; ./a.out 2>&1";
$process = proc_open($run_string,$descriptorspec,$pipes);
if (is_resource($process)) {
    //echo fgets($pipes[1])."<br/>";
    fwrite($pipes[0],'12');
    fclose($pipes[0]);
    while (!feof($pipes[1]))
        echo fgets($pipes[1])."<br/>";
    fclose($pipes[1]);
    proc_close($process);
}

运行C代码时,我想获取第一个STDOUT流,然后输入数字,然后获取第二个STDOUT流.但如果我将注释行取消注释,该页面将被阻止.

有没有办法解决这个问题?我怎样才能从管道读取而不是所有数据都放在那里?或者有更好的方法来编写这种交互式程序吗?

它更像是C或glibc问题.你必须使用fflush(stdout).

为什么?在终端中运行a.out和从PHP调用它有什么区别?

答:如果你在终端中运行a.out(正在使用stdin),那么glibc将使用行缓冲IO.但是如果你从另一个程序运行它(在这种情况下为PHP)并且它的stdin是一个管道(或者其他什么但不是tty),而glibc将使用内部IO缓冲.这就是为什么第一个fgets()如果没有注释就会阻塞.有关更多信息,请查看此article.

好消息:您可以使用stdbuf命令控制此缓冲.将$run_string更改为:

$run_string = "cd ".$addr_base.";stdbuf -o0 ./a.out 2>&1";

这是一个有效的例子.即使C代码不关心fflush(),因为它使用stdbuf命令:

启动子流程

$cmd = 'stdbuf -o0 ./a.out 2>&1';

// what pipes should be used for STDIN,STDOUT and STDERR of the child
$descriptorspec = array (
    0 => array("pipe","r"),1 => array("pipe","w"),2 => array("pipe","w")
 );

// open the child
$proc = proc_open (
    $cmd,$pipes,getcwd()
);

将所有流设置为非阻塞模式

// set all streams to non blockin mode
stream_set_blocking($pipes[1],0);
stream_set_blocking($pipes[2],0);
stream_set_blocking(STDIN,0);

// check if opening has succeed
if($proc === FALSE){
    throw new Exception('Cannot execute child process');
}

得到孩子的pid.我们以后需要它

// get PID via get_status call
$status = proc_get_status($proc);
if($status === FALSE) {
    throw new Exception (sprintf(
        'Failed to obtain status information '
    ));
}
$pid = $status['pid'];

轮询直到孩子终止

// now,poll for childs termination
while(true) {
    // detect if the child has terminated - the PHP way
    $status = proc_get_status($proc);
    // check retval
    if($status === FALSE) {
        throw new Exception ("Failed to obtain status information for $pid");
    }
    if($status['running'] === FALSE) {
        $exitcode = $status['exitcode'];
        $pid = -1;
        echo "child exited with code: $exitcode\n";
        exit($exitcode);
    }

    // read from childs stdout and stderr
    // avoid *forever* blocking through using a time out (50000usec)
    foreach(array(1,2) as $desc) {
        // check stdout for data
        $read = array($pipes[$desc]);
        $write = NULL;
        $except = NULL;
        $tv = 0;
        $utv = 50000;

        $n = stream_select($read,$write,$except,$tv,$utv);
        if($n > 0) {
            do {
                $data = fread($pipes[$desc],8092);
                fwrite(STDOUT,$data);
            } while (strlen($data) > 0);
        }
    }


    $read = array(STDIN);
    $n = stream_select($read,$utv);
    if($n > 0) {
        $input = fread(STDIN,8092);
        // inpput to program
        fwrite($pipes[0],$input);
    }
}

猜你在找的PHP相关文章