CUP Online Judge 的判题核心,基于 HUSTOJ 改造。
- 在线评测系统后端判题核心
- 判题集群中的独立判题节点
- 竞赛与作业系统的离线判题服务
- 本地或 CI 环境的提交回归验证
- 支持 MySQL 与离线 JSON 两种提交来源
- 支持多语言编译与运行,语言列表由
language.json管理 - 编译命令可配置,按语言 ID 在
compile.json中定义 - 支持普通判题与 Special Judge
- 支持相似度检测(sim)并可按配置开关
- 支持多测试点串行或并行执行
- 支持自定义输入的测试运行模式
- 支持时间与内存限制控制
- 支持 chroot + seccomp/ptrace 沙箱隔离
- 支持共享内存运行目录以提升性能
- 支持详细结果输出与运行信息回传
- 编译:使用
compile.json配置的编译命令,失败则直接返回 CE - 运行:子进程执行用户程序,父进程监控资源与退出状态
- 判题:普通题使用 Compare 模块比对输出;SPJ 走 Special Judge
- 入口实现参考 judge_solution、configure_and_run
- 单测点与多测点都经过统一的
judge_solution判定逻辑 all_test_mode为 1 时强制跑完所有测试点并按通过率汇总use_max_time控制用时统计为最大用时或累计用时- 并行判题通过线程池并发执行,入口为 runParallelJudge
- 运行前构建 chroot 环境,复制必要的系统库与 busybox,见 buildRuntime
- 运行时设置 CPU、文件大小、栈与内存限制,见 configure_and_run
use_ptrace=1使用 ptrace 监控;否则使用 seccomp 过滤系统调用- 进程数限制默认 1,见 setProcessLimit
- SPJ 文件位于
data/{problem_id}/,按spj/spj.js/spj.py优先级加载 - 使用
fork + pipe将 SPJ 结果从子进程回传,避免影响主判题进程 - SPJ 侧默认限制 CPU 5s、输出文件 50MB,并限制进程数
- 实现见 SpecialJudge::run
- 每种语言对应一套 syscall 白名单,定义在
model/judge/language/syscall/*/syscall32.h|syscall64.h - 初始化阶段调用 initSyscallLimits 将白名单写入计数器
- 未启用 ptrace 时使用 seccomp 过滤,构建逻辑见 seccomp_helper
- 通过
-record可记录 syscall 使用情况,并输出为可复用的白名单数组
- judge_client:主判题进程,负责编译、运行与判题
- wsjudged:判题进程包装器,设置资源限制后启动 judge_client
- shell/djudge.sh:调试用脚本示例
git clone- 执行仓库根目录下的
./build.sh - 配置
/home/judge/etc/config.json与/home/judge/etc/compile.json - 将
wsjudged移动到CUP-Online-Judge-Judger-Daemon-Service下 - 启动
CUP-Online-Judge-Judger-Daemon-Service
/home/judge:默认判题根目录(可通过参数覆盖)/home/judge/etc:配置目录/home/judge/submission:无 MySQL 模式下的提交描述文件/home/judge/data/{problem_id}:测试数据目录,存放.in/.out与spj文件/dev/shm/cupoj:共享内存运行目录(按配置启用)
judge_client 直接从数据库读取题目与提交信息。
judge_client \
-solution_id 1001 \
-runner_id 1 \
-dir /home/judge \
-judger_id j1通过 -no-mysql 指定为离线模式,提交信息从 JSON 文件或标准输入读取。
judge_client \
-solution_id 1001 \
-runner_id 1 \
-dir /home/judge \
-judger_id j1 \
-no-mysql默认读取 /home/judge/submission/{judger_id}.json,也可使用 -stdin 从标准输入读 JSON。
echo '{"language":1,"user_id":"u1","problem_id":1001,"spj":false,"memory_limit":256,"time_limit":1.5,"source":"int main(){}","solution_id":1001}' \
| judge_client -solution_id 1001 -runner_id 1 -dir /home/judge -judger_id j1 -no-mysql -stdinwsjudged <solution_id> <runner_id> <oj_home_dir> [DEBUG]参数由 detectArgType 识别,init_parameters 解析。
-solution_id <id>:提交 ID-runner_id <id>:判题进程 ID,用于区分运行目录-dir <path>:判题根目录,默认/home/judge-judger_id <id>:用于定位/submission/{id}.json的 ID-language <name>:设置语言名称(主要用于 syscall 统计输出)-no-mysql:不读数据库,走 JSON 提交模式-stdin:从标准输入读取提交 JSON(配合-no-mysql)-no_record:关闭 syscall 记录-record:开启 syscall 记录-admin:管理员模式,跳过相似度检测-no-sim:禁用相似度检测DEBUG:开启调试日志
兼容旧参数格式:
judge_client <solution_id> <runner_id> [oj_home_dir] [lang_or_debug] [DEBUG]
judge_client 实际读取的配置文件,加载逻辑见 init_mysql_conf。
hostname/username/password/db_name/port:数据库连接java_time_bonus/java_memory_bonus:Java 语言额外时空限java_xms/java_xmx:Java 编译运行 JVM 参数sim_enable:是否启用相似度检测full_diff:输出差异模式judger_name:上报使用的判题机标识shm_run:是否启用共享内存运行目录use_max_time:是否使用最大用时统计use_ptrace:是否启用 ptrace 监控all_test_mode:是否强制跑完所有测试点enable_parallel:是否启用并行判题
编译命令配置,judge_client 在编译阶段读取该文件,键为语言 ID,值为编译参数数组。
语言 ID 与语言名称映射,供运行时与编译阶段使用。
部署脚本会写入该文件,便于与其他服务保持一致;judge_client 主要读取 config.json。
字段定义见 SubmissionInfo。
{
"language": 1,
"user_id": "u1",
"problem_id": 1001,
"spj": false,
"memory_limit": 256,
"time_limit": 1.5,
"source": "int main(){}",
"solution_id": 1001
}若需要自定义输入模式,可额外提供:
{
"test_run": true,
"custom_input": "1 2\n"
}