gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 12:44:32 27 4
gpt4 key购买 nike

我想做一个网站,人们可以在线编译和运行他们的代码,因此我们需要找到一种交互方式让用户发送指令。

其实首先想到的是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(作为 tty 标准输入),则 glibc 将使用行缓冲 IO。但是如果你从另一个程序(在这种情况下是 PHP)运行它并且它的标准输入是一个管道(或者其他但不是 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, $descriptorspec, $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');
}

获取子进程号。我们稍后需要它

// 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'];

轮询直到 child 终止

// 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, $write, $except, $tv, $utv);
if($n > 0) {
$input = fread(STDIN, 8092);
// inpput to program
fwrite($pipes[0], $input);
}
}

关于php - 使用 proc_open 时从 STDIN 管道读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16351302/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com