欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OJ系统判题思路与代码实现

程序员文章站 2022-06-17 09:39:08
...

                                  OJ系统判题思路与代码实现

毕业设计做了个OJ系统,做得不好,打算重做,因为比较感兴趣,涉及到了许多东西,做系统的同时又把我在学校里学的东西给捡了回来,哈哈。

OJ系统判题思路与代码实现

一些相关说明:

  1. 主进程:调控子进程的程序。
  2. 子进程:执行源程序。
  3. 管道:(有名管道)子进程输出数据到管道中,主进程得以读取,半双工式环形缓冲区,半双工地读写决定了父/子只能有一个进程读/写,所以父子进程轮流阻塞。(听说,有名管道环形缓冲,待验证,待审查对程序影响力)。
  4. fork()创建子进程,复制一份父进程资源,然后创建子进程。这就造成了,两个进程执行同一份代码,通过判断pid值可知是父进程还是子进程。
  5. exec系列函数可执行源程序。子进程中调用exec系列函数,exec之后的代码不会被执行。
  6. setProcessLimit()自定义的,设定子进程资源限制,的函数,setrlimit()才是库函数(有问题,找男人)
  7. 捕获Linux进程信号量(略)
  8. 获取进程PCB控制块信息(运行时间,内存...),                                                                                                            wait4(...)函数,PCB进程信息存在于调用wait4(...)函数传如的某个结构体引用中。                                                               系统库结构体:struct rusage ru;                                                                                                                                        (运行时间)runTime = ru.ru_utime.tv_sec * 1000                                                                                                                                                      + ru.ru_utime.tv_usec / 1000                                                                                                                                                    + ru.ru_stime.tv_sec * 1000                                                                                                                                                      + ru.ru_stime.tv_usec / 1000;                                                                                                              (运行内存)runMemory = ru.ru_maxrss;
  9. 外部输入:                                                                                                                                                                        timeLimit:时间限制,                                                                                                                                                    memoryLimit: 内存限制,                                                                                                                                                codeType: 代码类型,                                                                                                                                                          test.in:标准输入文件,                                                                                                                                                        test.out:标准输出文件
  10. 内部输出:                                                                                                                                                                        exitValue: 进程退出值,                                                                                                                                              judgeResult: 评测结果,                                                                                                                                                    runTime: 运行时间,                                                                                                                                                              runMemory: 运行内存

人为规定,子进程返回0表示正常结束。

 

/*
工作空间默认为根目录:/
1.工作空间内存在test.in作为输入数据
2.工作空间内存在test.out作为输出数据
3.工作空间内存在Main作为待运行程序
4.执行:./runCode 2 500 c/c++
(第一个参数:2代表时间时限2s,第二个参数:500代表空间限制500MB, 第三个参数是代码类型)
5.将会输出的格式:
result:AC
message:%s(可能存在)
runTime:%d
runMemory:%ld
*/
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <map>
#define FIFO "stdout_fifo"
typedef long long ll;
typedef void(*func)();
using namespace std;
string Signal[][2] = {{"RE", ""}, {"RE", " "}, {"RE", ""}, {"RE", ""},{"RE", "非法指令"},
                        {"RE", ""}, {"RE", "异常中止"}, {"RE", ""}, {"RE", "浮点运算溢出"},{"RE", ""},
                        {"RE", ""}, {"RE", "非法内存地址引用"}, {"RE", ""}, {"RE", ""},{"RE", ""},
                        {"RE", ""}, {"RE", "栈错误"}, {"RE", ""}, {"RE", ""},{"RE", ""},
                        {"RE", ""}, {"RE", ""}, {"RE", ""}, {"RE", ""},{"TL", "时间超限"},
                        {"RE", ""}, {"TL", ""}, {"RE", ""}, {"RE", ""},{"RE", ""},
                        {"RE", ""}};
void setProcessLimit(int timeLimit, ll memoryLimit);
void runC_cppCode();
void runJavaCode();
void init();
map<string, func> Map;
int main(int argc, char *args[]) {
    chdir("/");
    init();
    int timeLimit = atoi(args[1]);
    long memoryLimit = atol(args[2]);
    memoryLimit <<= 10;
    unlink(FIFO);
    int res = mkfifo(FIFO, 0777);
    pid_t pid = fork();
    if(pid == 0) {
        freopen("test.in", "r", stdin);
        freopen("test.error", "w", stderr);
        int fd = open(FIFO, O_WRONLY);
        dup2(fd, 1);
        setProcessLimit(timeLimit, memoryLimit);
        Map[args[3]]();
    } else {
        FILE *output = fopen("test.out", "r");
        char buf[BUFSIZ], buf2[BUFSIZ];
        int fd = open(FIFO, O_RDONLY);
        int len = 0;
        int accept = 1;
        while((len = read(fd, buf, BUFSIZ)) != 0) {
            int len2 = fread(buf2, sizeof(char), len, output);
            if(len != len2) {
                accept = 0;
                break;
            } else {
                for(int i = 0; i < len; i++) {
                    if(buf[i] ^ buf2[i]) {
                        accept = 0;
                        break;
                    }
                }
                if(accept == 0) {
                    break;
                }
            }
        }
        if(!(feof(output))) {
            fgetc(output);
        }
        accept?(accept=feof(output)): kill(pid, SIGKILL);
        fclose(output);
        int status, runTime;
        long runMemory;
        struct rusage ru;
        wait4(pid, &status, WUNTRACED, &ru);
        runTime = ru.ru_utime.tv_sec * 1000
                   + ru.ru_utime.tv_usec / 1000
                   + ru.ru_stime.tv_sec * 1000
                   + ru.ru_stime.tv_usec / 1000;
        runMemory = ru.ru_maxrss;
        if(accept == 0) {
            cout << "result:WA\n" <<endl;
        } else {
            if(WIFEXITED(status)) {
               cout << "result:" << (accept? (runMemory <= memoryLimit? "AC": "ML"): "WA") << endl;
            } else {
               int status2 = WIFSIGNALED(status)? WTERMSIG(status): (WIFSTOPPED(status)? WSTOPSIG(status): 0);
               if(status2 == SIGSEGV && runMemory > memoryLimit) {
                   cout << "result:ML" << endl;
               } else {
                   cout << "result:" << Signal[status2][0] << endl;
                   cout << "message:" << Signal[status2][1] << endl;
               }
            }
        }
        cout << "runTime:" << runTime <<endl;
        cout << "runMemory:" << runMemory <<endl;
    }
    return 0;
}
void setProcessLimit(int timeLimit, ll memoryLimit) {
    struct rlimit rl;
    rl.rlim_cur = timeLimit;
    rl.rlim_max = timeLimit + 1;
    setrlimit(RLIMIT_CPU, &rl);
    rl.rlim_cur = (memoryLimit + (memoryLimit >> 1)) << 10;
    rl.rlim_max = (memoryLimit << 1) << 10;
    setrlimit(RLIMIT_DATA, &rl);
}
void runC_cppCode() {
    if(access("Main", F_OK) == -1) {
        cerr << "Main没有找到" <<endl;
    } else {
        char *args[] = {NULL};
        execv("Main", args);
    }
}

void runJavaCode() {
    if(access("Main.class", F_OK) == -1) {
        cerr << "Main.class没有找到" <<endl;
    } else {
        char *args[] = {"java", "Main", NULL};
        execvp("java", args);
    }
}
void init() {
    Map["c"] = runC_cppCode;
    Map["c/c++"] = runC_cppCode;
    Map["java"] = runJavaCode;
}