在Linux上创建一个PHP在线分级系统:EXE​​C行为,进程ID,和grep [英] Creating a PHP Online Grading System on Linux: exec Behavior, Process IDs, and grep

查看:180
本文介绍了在Linux上创建一个PHP在线分级系统:EXE​​C行为,进程ID,和grep的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写使用PHP和MySQL一个简单的在线法官(A code分级系统)。这需要提交codeS在C ++和Java,编译它们,并测试它们。

I am writing a simple online judge (a code grading system) using PHP and MySQL. It takes submitted codes in C++ and Java, compiles them, and tests them.

这是Apache的一个老版本的Ubuntu上运行PHP 5.2。

This is Apache running PHP 5.2 on an old version of Ubuntu.

我有一个PHP程序,通过

I have a php program that loops infinitely, calling another php program by

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

在进行每隔十分之一秒。让我们把第一个 looper.php ,第二个 grade.php 。 (检查点:<?code> grade.php 应该彻底完成之前运行for循环下去,正确的)

every tenth of a second. Let's call the first one looper.php and the second one grade.php. (Checkpoint: grade.php should completely finish running before the "for" loop continues, correct?)

grade.php 拉最早提交code,它需要从MySQL数据库分级,提出了code在一个文件中(的考验。[CPP / JAVA] ),并呼吁在继承,命名为 compile.php 2的其他PHP程序和 test.php的,就像这样:

grade.php pulls the earliest submitted code that needs to be graded from the MySQL database, puts that code in a file (test.[cpp/java]), and calls 2 other php programs in succession, named compile.php and test.php, like so:

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

(检查点: compile.php 应该彻底完成之前,for循环调用 test.php的甚至跑步启动时,正确吗?)

(Checkpoint: compile.php should completely finish running before the "for" loop calling test.php even starts, correct?)

compile.php 然后在测试编译程序。[CPP / JAVA] 作为背景过程的。现在,让我们假设它的编译Java程序和 test.java 位于子目录。我现在有

compile.php then compiles the program in test.[cpp/java] as a background process. For now, let's assume that it's compiling a Java program and that test.java is located in a subdirectory. I now have

//...
//$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 应运行作为后台进程......它似乎喜欢它作品。在 $退出应该在 $抢夺的进程id 的javac 出来[0]

in compile.php. It's redirecting the output from javac, so javac should be running as a background process... and it seems like it works. The $out should be grabbing the process id of javac in $out[0].

我想停下来编译如果由于某种原因需要编译超过10秒,我想结束 compile.php 如果程序停止前10秒编译。因为 EXEC(javac的... 我上面堪称是一个后台进程(或者是什么呢?),我没有当它已经不看完成的办法知道进程ID,这应该被保存在 $退出较早右键后,在 compile.php ,我这样做用10秒循环调用 EXEC(ps的斧头| grep的[PID] * javac的。); ,看到如果PID仍然存在:

I want to stop compiling if for some reason compiling takes more than 10 seconds, and I want to end compile.php if the program stops compiling before 10 seconds. Since the exec("javac... I called above is a background process (or is it?), I have no way of knowing when it has completed without looking at the process id, which should have been stored in $out earlier. Right after, in compile.php, I do this with a 10 second loop calling exec("ps ax | grep [pid].*javac"); and seeing if the pid still exists:

//...
$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程序。我认为,在循环被旁路出于某种原因,虽然这可能不是这样的。在其他时间,整个分级制度按预期工作。

... which doesn't seem to be working. At least some of the time. Often, what happens is test.php starts running before the javac even stops, resulting in test.php not being able to find the main class when it tries to run the java program. I think that the loop is bypassed for some reason, though this may not be the case. At other times, the entire grading system works as intended.

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

Meanwhile, test.php also uses the same strategy (with the X-second loop and the grep) in running a program in a certain time limit, and it has a similar bug.

我认为错误在于的grep 没有找到的javac的PID 即使 javac的仍在运行,从而导致10个第二环路早期断裂。你可以发现一个明显的错误?更谨慎的错误?是否与我的 EXEC 的使用有问题?是否与 $出了问题?或者是完全不同的事情发生?

I think the bug lies in the grep not finding javac's pid even when javac is still running, resulting in the 10 second loop breaking early. Can you spot an obvious bug? A more discreet bug? Is there a problem with my usage of exec? Is there a problem with $out? Or is something entirely different happening?

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

Thank you for reading my long question. All help is appreciated.

推荐答案

我刚刚想出了这个code,它会运行一个进程,如果它的运行速度比 $超时时间终止秒。如果在超时前结束,它会在 $输出键,退出状态节目输出在 $ RETURN_VALUE

I just came up with this code that will run a process, and terminate it if it runs longer than $timeout seconds. If it terminates before the timeout, it will have the program output in $output and the exit status in $return_value.

我已经测试过它,它似乎运作良好。希望你可以使其适应您的需求。

I have tested it and it seems to work well. Hopefully you can adapt it to your needs.

<?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()

这篇关于在Linux上创建一个PHP在线分级系统:EXE​​C行为,进程ID,和grep的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆