Intereting Posts
PHP Вставка данных JSON в базу данных MySQL с Android Добавить шесть месяцев в php Невозможно запросить разрешение на использование Facebook Login API «publish_actions» даже для основного администратора php scandir -> поиск файлов / каталогов Максимальное количество ключей для массива в php Тестирование прикомандированного вызова метода в Mockery Как безопасно хранить файлы cookie, содержащие конфиденциальные данные, в PHP? php -> mysql_connect -> дочерний процесс, завершившийся со статусом 255 – Перезапуск CodeIgniter: класс / библиотека, чтобы помочь получить метатеги с веб-страницы? Вход в удаленный сайт с PHP cURL Как обновить токен с клиентом API Google? Как выбрать год и месяц из атрибутов created_at таблицы базы данных в laravel 5.1? Невозможно использовать возвращаемое значение метода в контексте записи Как разобрать этот json с php? PHP – как перевернуть строки и столбцы 2D-массива

Чтение из трубы STDIN при использовании proc_open

Я пытаюсь создать сайт, на котором люди могут компилировать и запускать свой код в Интернете, поэтому нам нужно найти интерактивный способ отправки пользователями инструкций.

Фактически, первое, что приходит в голову, это exec() или system() , но когда пользователи хотят вводить sth, этот способ не будет работать. Поэтому мы должны использовать 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. Но если у меня есть прокомментированная строка без комментариев, страница будет заблокирована.

Есть ли способ решить проблему? Как я могу читать из трубы, пока не все данные были там поставлены? Или есть лучший способ написать такую ​​интерактивную программу?

Related of "Чтение из трубы STDIN при использовании proc_open"

Это скорее проблема C или glibc . Вам нужно будет использовать fflush(stdout) .

Зачем? И в чем разница между запуском a.out в терминале и вызовом его из PHP?

Ответ. Если вы запустите a.out в терминале (будучи stdin tty), тогда glibc будет использовать строку с буферизацией ввода-вывода. Но если вы запустите его из другой программы (в этом случае PHP), и это stdin – это канал (или что-то другое, но не tty), чем glibc будет использовать внутреннюю буферизацию ввода-вывода. Вот почему первые 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); } }