gpt4 book ai didi

php - 在 Linux : exec Behavior, 进程 ID 和 grep 上创建 PHP 在线评分系统

转载 作者:搜寻专家 更新时间:2023-10-31 21:15:40 25 4
gpt4 key购买 nike

背景

我正在使用PHP和MySQL编写一个简单的在线判断(代码评分系统)。它接受提交的 C++ 和 Java 代码,编译并测试它们。

这是在旧版本的 Ubuntu 上运行 PHP 5.2 的 Apache。

我现在在做什么

我有一个无限循环的 php 程序,通过调用另一个 php 程序

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

每十分之一秒。我们将第一个称为 looper.php,将第二个称为 grade.php。 (检查点:grade.php 应该在“for”循环继续之前完全运行完毕,对吗?)

grade.php 从 MySQL 数据库中提取最早提交的需要评分的代码,并将该代码放入文件中 (test.[cpp/java]) ,并依次调用另外两个 php 程序,分别命名为 compile.phptest.php,如下所示:

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

(检查点:compile.php 应该在调用 test.php 的“for”循环开始之前完全运行,对吗?)

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 应该在 $out[0] 中获取 javac 的进程 ID。

真正的问题

如果由于某种原因编译时间超过 10 秒,我想停止编译,如果程序在 10 秒之前停止编译,我想结束 compile.php。由于我上面调用的 exec("javac... 是一个后台进程(或者是吗?),我无法在不查看进程 ID 的情况下知道它何时完成,它应该有之前存储在 $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);
//...

... 这似乎不起作用。至少在某些时候。通常,发生的情况是 test.phpjavac 甚至停止之前就开始运行,导致 test.php 无法找到主类当它尝试运行 java 程序时。我认为由于某种原因绕过了循环,尽管情况可能并非如此。在其他时候,整个评分系统会按预期工作。

同时,test.php也使用相同的策略(X秒循环和grep)在一定的时间限制运行程序,并且有类似的错误。

我认为错误在于 grep 没有找到 javac 的 pid,即使 javac 仍在运行,导致 10 秒循环提前中断。你能发现一个明显的错误吗?一个更谨慎的错误?我对 exec 的使用有问题吗? $out 有问题吗?还是发生了完全不同的事情?

感谢您阅读我的长问题。感谢所有帮助。

最佳答案

我刚刚想出了这段代码,它会运行一个进程,如果它运行的时间超过 $timeout 秒就会终止它。如果它在超时之前终止,它将在 $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()

关于php - 在 Linux : exec Behavior, 进程 ID 和 grep 上创建 PHP 在线评分系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9356250/

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