2013-05-03 3 views
7

사람들이 코드를 온라인으로 컴파일하고 실행할 수있는 웹 사이트를 만들고자 노력 중이므로 사용자가 지침을 보낼 수있는 대화식 방법을 찾아야합니다.proc_open을 사용할 때 STDIN 파이프에서 읽기

실제로 처음 생각한 것은 exec() 또는 system()이지만 사용자가 sth을 입력하려고하면이 방법이 작동하지 않습니다. 따라서 proc_open()을 사용해야합니다.

예를 들어

, C 코드를 실행할 때 내가, 내가 처음 STDOUT 스트림을 얻으려면이

$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); 
} 

처럼 proc_open()를 사용하는 다음 코드

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

, 입력 수 두 번째 STDOUT 스트림을 가져옵니다. 그러나 주석 처리 된 행의 주석을 제거한 경우 페이지가 차단됩니다.

문제를 해결할 방법이 있습니까? 모든 데이터가 거기에 놓여있는 것은 아니지만 어떻게 파이프에서 읽을 수 있습니까? 아니면 이런 종류의 양방향 프로그램을 작성하는 더 좋은 방법이 있습니까?

+0

가 종료 될 때까지 우리는 나중에

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

설문 조사를해야합니까? 이런 식으로 사용자는 서버의 'STDIN'에 직접 액세스하지 못하는 것 같습니다. – Passerby

+0

@Passerby 사용자가 버튼을 눌러 컴파일하고 _x_를 입력하여 서버로 보냅니다. 그러나 _x_를 입력하기 전에 서버는 먼저 STDIN에서 스트림을 가져 와서 웹 사이트에 보내야하므로 사용자는 _x_을 입력해야한다는 것을 알 수 있습니다.문제는 서버가 그 순간에 스트림을 가져올 수 없다는 것입니다. – dahui

답변

18

더 많은 것은 C 또는 glibc 문제입니다. fflush(stdout)을 사용해야합니다.

왜? 터미널에서 a.out을 실행하고 PHP에서 호출하는 것의 차이점은 무엇입니까?

응답 : 터미널에 a.out을 실행하면 (stty가 tty 인 경우) glibc는 줄 버퍼 된 IO를 사용합니다. 하지만 다른 프로그램 (이 경우에는 PHP)에서 실행하면 glibc가 내부 IO 버퍼링을 사용하는 것보다 stdin이 파이프 (또는 tty가 아닌 다른 것)가됩니다. 따라서 주석 처리되지 않은 경우 첫 번째 fgets() 블록이 발생합니다. 자세한 내용은 article을 확인하십시오.

좋은 소식 : stdbuf 명령을 사용하여이 버퍼링을 제어 할 수 있습니다.

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

가 작동 예제를 제공 : $run_string에 변경합니다. 그것은 stdbuf 명령 사용으로 C 코드에 대한 fflush()을 걱정하지 않아도 사용 :

가 하위 프로세스를

$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를 얻을 수 . 아이가 웹 사이트에서 상호 작용하는 사용자 "사용자가"

// 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); 
    } 
} 
+1

고마워요 ~하지만 C 코드는 사용자의 것이며, 그는 단지'Enter'를 눌러 컴파일하고 실행합니다. _test.php_의 일부를 변경하여 작동하게 할 수 있습니까? – dahui

+0

흥미로운 문제! :) 이것을 조사 할 것이다. – hek2mgl

+0

고마워요! 그것은 나를 오랫동안 귀찮게했습니다. – dahui