在Linux上创建PHP在线评分系统:exec行为、进程ID和grep 背景

在Linux上创建PHP在线评分系统:exec行为、进程ID和grep 背景,php,apache,ubuntu,exec,javac,Php,Apache,Ubuntu,Exec,Javac,我正在使用PHP和MySQL编写一个简单的在线评委(代码评分系统)。它采用C++和java中的提交代码,编译它们,并对它们进行测试。 这是Apache在旧版本的Ubuntu上运行PHP5.2 我现在在做什么 我有一个无限循环的php程序,通过 //for(infinity) exec("php -f grade.php"); //... 每十分之一秒。让我们调用第一个looper.php和第二个grade.php。(检查点:grade.php应该在“for”循环继续之前完全完成运行,对

我正在使用PHP和MySQL编写一个简单的在线评委(代码评分系统)。它采用C++和java中的提交代码,编译它们,并对它们进行测试。

这是Apache在旧版本的Ubuntu上运行PHP5.2

我现在在做什么 我有一个无限循环的php程序,通过

//for(infinity)
    exec("php -f grade.php");
//...
每十分之一秒。让我们调用第一个
looper.php
和第二个
grade.php
。(检查点:
grade.php
应该在“for”循环继续之前完全完成运行,对吗?)

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

//...
exec("php -f compile.php");
//...
//for([all tests])
    exec("php -f test.php");
//...
(检查点:
compile.php
应该在“for”循环调用
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
应该在
$out[0]
中获取
javac
的进程id

真正的问题 如果出于某种原因编译时间超过10秒,我想停止编译;如果程序在10秒之前停止编译,我想结束
compile.php
。由于我上面调用的
exec(“javac…”
是一个后台进程(或者它是吗?),我无法知道它何时完成,除非查看进程id,它应该在前面存储在
$out
中。紧接着,在
compile.php
中,我通过10秒的循环调用
exec(“ps ax | grep[pid])查看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\u compile=false;
while((microtime(true)-$start\u time<10)和&!$done\u编译){
usleep(20000);//检查之间只需睡眠0.02秒
未结算($grep);
exec(“ps ax | grep”。$pid.*javac,$grep);
$found\u process=false;
//循环查看grep的结果
while(!$found\u进程和列表(,$proc)=每个($grep)){
$boom=爆炸(“,$proc”);
$npid=(int)$boom[0];
如果($npid==$pid)
$found\u process=true;
}
$done\u compile=!$found\u进程;
}
如果(!完成_编译)
执行(“杀死-9”。$pid);
//...
…这似乎不起作用。至少在某些时候。通常情况下,发生的情况是
test.php
javac
甚至停止之前就开始运行,导致
test.php
在尝试运行java程序时无法找到主类。我认为由于某种原因,循环被绕过了,尽管这可能是错误的事实并非如此。在其他情况下,整个评分系统按预期工作

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

我认为缺陷在于
grep
即使在
javac
仍在运行时,也找不到
javac
的pid,导致10秒循环提前中断。你能发现一个明显的缺陷吗?一个更谨慎的缺陷吗?我使用
exec
有问题吗?
$out
有问题吗?或者是其他什么问题吗发生了什么事


感谢您阅读我的长问题。非常感谢您提供的所有帮助。

我刚刚提出了一段代码,该代码将运行一个进程,并在其运行时间超过
$timeout
秒时终止它。如果它在超时之前终止,它将在
$output
中显示程序输出,在
$return\u 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";
}

由于没有人回答,下面是我问题的简短版本:如何使用PHP创建后台进程,以便1)我可以在X秒后杀死它,2)我可以在10秒前判断过程是否自行结束?谢谢你的回答!我现在采取不同的方法,但如果我的新方法不起作用,我将以你的解决方案为基础。似乎我不再需要更好的答案,所以我会检查你的。你好,这似乎不起作用如果他们提交具有无限循环的解决方案:(