我试图做一个网站,人们可以编译和联机运行他们的代码,因此,我们需要找到用户发送指令的互动方式。
其实,第一个想到的是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流,并输入号码,然后获得第二个标准输出流。 但是,如果我有注释行注释掉,该页面将被阻止。
有没有办法解决这个问题的方法吗? 我怎样才能从管道中读取,而不是所有的数据已被放在那里? 还是有更好的方式来写这种互动节目的?
它更是一个C
或glibc
问题。 你将不得不使用fflush(stdout)
。
为什么? 什么是运行之间的差异a.out
在终端和PHP调用它?
答:如果您运行a.out
在终端(标准输入是一个tty),那么glibc就可以使用缓冲IO线。 但如果你从另一个程序(PHP在这种情况下)运行它,它的标准输入比glibc就可以使用内部IO缓冲管(或任何但是不是tty)。 这就是为什么第一fgets()
块,如果注释。 欲了解更多信息检查此文章 。
好消息:你可以控制使用该缓冲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');
}
让孩子的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, $write, $except, $tv, $utv);
if($n > 0) {
$input = fread(STDIN, 8092);
// inpput to program
fwrite($pipes[0], $input);
}
}