Создание онлайн-системы классификации PHP в Linux: exec Behavior, Process ID и grep

Задний план

Я пишу простой онлайн-судья (система оценки кода) с использованием PHP и MySQL. Он принимает представленные коды на C ++ и Java, компилирует их и тестирует их.

Это Apache работает на PHP 5.2 на старой версии Ubuntu.

Что я сейчас делаю

У меня есть php-программа, которая бесконечно петляет, вызывая другую php-программу

//for(infinity) exec("php -f grade.php"); //... 

каждую десятую секунды. Назовем первый looper.php а второй – grade.php . (Контрольная точка: grade.php должен полностью завершить работу до того, как цикл «for» продолжит, исправьте?)

grade.php вытаскивает самый ранний представленный код, который необходимо оценивать из базы данных MySQL, помещает этот код в файл ( test.[cpp/java] ) и вызывает 2 других php-программы подряд, с именем compile.php и test.php , вот так:

 //... exec("php -f compile.php"); //... //for([all tests]) exec("php -f test.php"); //... 

(Контрольная точка: compile.php должен полностью завершить работу до того, как цикл «for» compile.php test.php даже начнется, правильно?)

compile.php затем компилирует программу в test.[cpp/java] как фоновый процесс . Пока давайте предположим, что он компилирует программу Java и что test.java находится в подкаталоге. У меня теперь есть

 //... //$dir = "./sub/" or some other subdirectory; this may be an absolute path $start_time = microtime(true); //to get elapsed compilation time later exec("javac ".$dir."test.java -d ".$dir." 2> ".$dir ."compileError.txt 1> ".$dir."compileText.txt & echo $!", $out); //... 

в compile.php . Он перенаправляет вывод из javac , поэтому javac должен работать как фоновый процесс … и похоже, что он работает. $out должен захватывать идентификатор процесса javac в $out[0] .

Реальная проблема

Я хочу прекратить компиляцию, если по какой-то причине компиляция занимает более 10 секунд, и я хочу завершить compile.php если программа прекращает компиляцию до 10 секунд. Поскольку exec("javac... я вызываю выше – фоновый процесс (или это?), Я не знаю, когда он завершил, не обращаясь к идентификатору процесса, который ранее был сохранен в $out . Сразу после этого, в compile.php , я делаю это с 10-секундным циклом, вызывающим exec("ps ax | grep [pid].*javac"); и, если pid все еще существует:

 //... $pid = (int)$out[0]; $done_compile = false; while((microtime(true) - $start_time < 10) && !$done_compile) { usleep(20000); // only sleep 0.02 seconds between checks unset($grep); exec("ps ax | grep ".$pid.".*javac", $grep); $found_process = false; //loop through the results from grep while(!$found_process && list(, $proc) = each($grep)) { $boom = explode(" ", $proc); $npid = (int)$boom[0]; if($npid == $pid) $found_process = true; } $done_compile = !$found_process; } if(!done_compile) exec("kill -9 ".$pid); //... с //... $pid = (int)$out[0]; $done_compile = false; while((microtime(true) - $start_time < 10) && !$done_compile) { usleep(20000); // only sleep 0.02 seconds between checks unset($grep); exec("ps ax | grep ".$pid.".*javac", $grep); $found_process = false; //loop through the results from grep while(!$found_process && list(, $proc) = each($grep)) { $boom = explode(" ", $proc); $npid = (int)$boom[0]; if($npid == $pid) $found_process = true; } $done_compile = !$found_process; } if(!done_compile) exec("kill -9 ".$pid); //... 

… который, похоже, не работает. По крайней мере, некоторое время. Часто случается, что test.php запускается до того, как javac даже остановится, в результате чего test.php не сможет найти основной класс при попытке запустить java-программу. Я думаю, что цикл почему-то исключен, хотя это может быть и не так. В других случаях вся система сортировки работает по назначению.

Между тем, test.php также использует ту же стратегию (с циклом X-second и grep) при запуске программы в определенный срок, и у нее есть аналогичная ошибка.

Я думаю, что ошибка заключается в том, что grep не обнаруживает javac , даже когда javac все еще работает, в результате чего 10-секундный цикл прерывается раньше. Можете ли вы заметить очевидную ошибку? Более сдержанная ошибка? Есть ли проблема с моим использованием exec ? Есть ли проблема с $out ? Или что-то совсем другое происходит?

Спасибо, что прочитали мой длинный вопрос. Вся помощь приветствуется.

Related of "Создание онлайн-системы классификации PHP в Linux: exec Behavior, Process ID и grep"

Я просто придумал этот код, который будет запускать процесс, и прекратить его, если он будет работать дольше, чем $timeout seconds. Если он заканчивается до истечения таймаута, он будет иметь выход программы в $output и статус выхода в $return_value .

Я тестировал его, и он работает хорошо. Надеюсь, вы сможете приспособить его к вашим потребностям.

 <?php $command = 'echo Hello; sleep 30'; // the command to execute $timeout = 5; // terminate process if it goes longer than this time in seconds $cwd = '/tmp'; // working directory of executing process $env = null; // environment variables to set, null to use same as PHP $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to ); // start the process $process = proc_open($command, $descriptorspec, $pipes, $cwd, $env); $startTime = time(); $terminated = false; $output = ''; if (is_resource($process)) { // process was started // $pipes now looks like this: // 0 => writeable handle connected to child stdin // 1 => readable handle connected to child stdout // Any error output will be appended to /tmp/error-output.txt // loop infinitely until timeout, or process finishes for(;;) { usleep(100000); // dont consume too many resources $stat = proc_get_status($process); // get info on process if ($stat['running']) { // still running if (time() - $startTime > $timeout) { // check for timeout // close descriptors fclose($pipes[1]); fclose($pipes[0]); proc_terminate($process); // terminate process $return_value = proc_close($process); // get return value $terminated = true; break; } } else { // process finished before timeout $output = stream_get_contents($pipes[1]); // get output of command // close descriptors fclose($pipes[1]); fclose($pipes[0]); proc_close($process); // close process $return_value = $stat['exitcode']; // set exit code break; } } if (!$terminated) { echo $output; } echo "command returned $return_value\n"; if ($terminated) echo "Process was terminated due to long execution\n"; } else { echo "Failed to start process!\n"; } 

Ссылки: proc_open () , proc_close () , proc_get_status () , proc_terminate ()