OnlineJudge判题平台 ——后台流程

判题分两部分

1.judged为服务进程,ddaemon。负责轮询数据库,提取判题队发现新任务时产生judge_client进程

2.judge_client进程为实际判题程序,负责准备运行环境、数据,运行并监控目标程序的系统调用,采集运行指标,判断运行结果。

Judged流程

OnlineJudge判题平台——后台流程_第1张图片

初始化:

1.创建子进程pid_judged,并设置为会话的领头进程(umask(0),close(0~2)

2.变当前工作目录为“/home/judge

3.pid_judged写入文件“/home/judge/etc/judge.pid,并且加入写锁(用来检查该服务进程是否已经在执行)

4.设置SIGQUIT,SIGKILL,SIGTERM来触发call_for_exit函数结束服务进程的执行

轮询:

5.连接数据库(host_name,user_name,password,db_name,port_number

6.轮询数据库中的solution表,将未测评的用户提交扔进任务队列

7.从任务队列提取任务给“判题进程”(子进程),并标记该任务为“正在测评”

8.使用rlimit构体和setrlimit()设置“判题进程”的允许的最大CPU行时间,可以创建的最大文件字节数,可用内存最大字节数,可拥有最大进程数(200

9.调用execl数执行/usr/bin/judge_client,并传递参数为任务id,judge_client_id


Judge_client流程

OnlineJudge判题平台——后台流程_第2张图片

初始化:

1.设置工作目录为/home/judge

2.获取任务id,judge_client_id

3.连接数据库

4.设置工作目录为workdir=/home/judge/run?

5.解挂文件系统:workdir/proc,删除workdir/下的所有文件和目录

编译:

6.查询数据库获取题目id,用户id,语言类型

7.询数据库获取 题目规定的最大运行时间time_lmt,最大mem_lmt

8.查询数据库获取用户提交的代码并写入Main.cc文件

9.创建子进程使用execvp函数执行g++命令编译Main.cc文件,并将编译信息写入ce.txt文件中

10.父进程调用waitpid函数来等待子进程编译结束,通过查看ce.txt文件来判断是否为编译错误,若为编译错误则退出

提取输入输出数据

11./home/judge/data/取题目输入数据文件并保存路径为infile

12.将题目输入数据拷贝到workdir/data.in文件

13.获取题目输出数据文件并保存路径outfile,创建user.out文件并保存路径为userfile

运行目标程序:

14.创建子进程pidApp,重定向输入输出流,从data.in读入数据,运行结果写入user.out,运行错误信息写入error.out

15.使用ptrace(PTRACE_TRACEME, 0, NULL, NULL)函数来使父进程跟踪自己

16.设置工作目录workdir为根目录(chroot()),提高系统安全性

17.使用rlimit结构体设置进程的CPU运行时间,可以创建最大文件字节数,可拥有最大进程数(1),拥有的堆栈数,可用内存数

18.alarm()设置定时器

19.调用execl使进程执行Main(目标程序)

监控目标程序:

20.父进程调用wait4(pidApp,&status,0,&ruse)来获取子进程暂停或中止时的返回状态和查看进程的使用资源情况

21.查看workdir/pro/pidApp/status里的VmPeak:属性的值,这个值是进程使用的最大内存数,如果超出限制,则ptrace(PTRACE_KILL,pidApp,NULL,NULL);退出监控

22.调用WIFEXITED(status)判断子进程是否正常结束

23.查看error.out文件,如果有内容则为运行错误,KILL掉子进程,监控结束

24.如果userfilesize > outfilesize*2,则为结果超出限制,kill掉子进程,监控结束

25.exitcode=WEXITSTATUS(status)获取子进程exit()返回的结束代码,如果为05则为正常,否则为异常结束,根据信号的类别给出相应错误,比如SIGALRM为计时器时间到了信号,SIGXCPU为运行时间到了信号,SIGXFSZ为输出文件大小超出信号等等

26.调用WIFSIGNALED(status)检查是否为异常结束(子进程通过信号结束)

27.调用sig=WTERMSIG(status)取得使子进程结束的信号编号,根据sig的类别给出相应的错误

28.调用ptrace(PTRACE_GETREGS,pidApp,NULL,&reg)来取得进程的寄存器信息(reg.REG_SYSCALL),检查系统调用函数的使用情况,若为禁止的系统调用函数则KILL掉子进程并将运行错误写入结果AC_status,结束监控

29.调用ptrace(PTRACE_SYSCALL,pidApp,NULL,NULL)使子进程继续执行

30.在监控结束以后统计usedtime+=ruse.ru_utime(用户使用时间)+ruse.ru_stime(系统使用时间)

31.根据AC_status的值更新数据库


感谢hustoj开源代码及作者!









你可能感兴趣的:(开源代码)