当前位置: 首页 > wzjs >正文

生鲜农产品网站建设搜搜

生鲜农产品网站建设,搜搜,方向专业网站制作咨询,阿里云轻云服务器可以放多个网站啊怎么做HUSTOJ 判题机源码解读系列01 1. HUSTOJ HUSTOJ 是一个开源的在线判题系统,很早之前就已经开发了,在源码里我甚至看到过 2008 年的 Git 提交记录(也有可能没这么早,记不太清了),时至本篇博客编写之际&…

HUSTOJ 判题机源码解读系列01

1. HUSTOJ

HUSTOJ 是一个开源的在线判题系统,很早之前就已经开发了,在源码里我甚至看到过 2008 年的 Git 提交记录(也有可能没这么早,记不太清了),时至本篇博客编写之际,2025 年了作者还在维护更新。

感谢 zhblue(原作者)贡献的代码。

本系列文章会介绍 HUSTOJ 的核心部分——判题机。

主要内容包括以下两个部分:

  1. 判题机的整体设计
  2. 使用到的核心技术

会介绍源码中的关键点和难点,但不会逐行的对源码进行注释。

希望本系列文章能够帮助到想要学习 OJ 的判题机怎么开发的同学(并非零基础,操作系统最基础的知识,例如进程、文件 IO 等概念需要知道)。

2. 源码文件分析

HUSTOJ 判题机的源码默认安装在 /home/judge/src/core 目录下,其结构目录如下:

./core
├── judge_client
│   ├── getindocker.sh
│   ├── judge_client
│   ├── judge_client.cc
│   ├── judge_client.http
│   ├── judge_client.o
│   ├── log.txt
│   ├── loggedcalls.sh
│   ├── makefile
│   ├── ncalls.h
│   ├── nohup.out
│   ├── okcalls.h
│   ├── okcalls32.h
│   ├── okcalls64.h
│   ├── okcalls_aarch64.h
│   ├── okcalls_arm.h
│   └── okcalls_mips.h
├── judged
│   ├── judged
│   ├── judged.cc
│   ├── judged.http
│   ├── judged.o
│   ├── judgehub
│   ├── judgehub.cc
│   ├── judgehub2.cc
│   └── makefile
├── make.sh
└── sim├── sim.sh└── sim_3_01

在这么多文件中,我们只需要关心 judge_client.cc 文件和 judge.cc 这两个核心文件。

除此之外,其它的文件与判题机的核心原理关系不大,这里只需要简单了解一下。

  • okcall 系列文件可以当作配置文件,其中是各种编程语言在各种平台上允许的系统调用编号
  • sim系列文件是用来做代码查重的功能的
  • judgehub 系列文件是用于 Saas 服务的

回到核心文件 judge_client.ccjudge.cc,这两个 C 语言文件会被分为编译为两个可执行文件,并一起放到 /usr/bin/目录下。

./core/make.sh 是编译脚本,执行这个脚本就可以编译了。

编译结果:

  • judge_client.cc -> /usr/bin/judge_client
  • judge.cc-> /usr/bin/judged

编译成功后,执行命令(开启判题服务):

sudo judged

/usr/bin/judged 可执行文件就会从磁盘加载到内存中,成为一个守护进程。现在我们称这个守护进程为 judged

执行命令(关闭判题目服务):

sudo pkill -9 judged

就可以杀掉 judged 进程。

当判题服务启动的时候,judgedjudge_client 就可以相互配合进行判题了,具体情况是:

  • judged 负责接取判题任务,然后将任务转交给 judge_client
  • judged_client 拿到任务后开始进行判题

可以将 judged 当做一个经理,只管接活,手底下可能会有几个 judged_client 去干活(具体多少个可以通过配置文件配置)。

3. 判题流程(简略版)

因为 judged 是通过轮询数据库接取判题任务的(也支持 HTTP 判题,由于默认是数据库判题,这里就省略 HTTP 判题了),所以这里需要了解一下系统里和判题有关的几张表的相关信息。

  • problem:题目信息表,保存题目的标题、描述、输入输出示例、难度、限制等信息
  • solution:提交信息表,记录用户针对某道题目的提交记录、判题结果、使用语言、提交时间等
  • source_code:源代码表,保存对应提交的代码内容
  • compileinfo:编译信息表,主要存储编译错误信息
  • runtimeinfo:运行时错误信息表,主要存储程序运行出错时的错误信息

这几个表之间通过 solution_idproblem_id 等关键字段进行关联。

表的结构过长,放在文中影响文章结构,文章跨度过大,所以表结构和字段解释都放在文章末尾,这里知道每张表大致是什么就可以了,并不用精确到字段。

接下来是用户提交代码到判题机评测代码到最终用户查询到判题的结果的大致过程(后续系列会通过源码精讲)。

第一步:用户提交代码

用户点击题库中的题目,选了一道题目,使用一种编程语言,写了代码,然后点击提交,这里提交时候后端会得到的数据有:

  • problem_id:问题 ID
  • user_id: 用户 ID
  • language:编程语言编号,例如(0 = C, 1 = C++, 2 = Java)
  • source:用户编写的代码

第二步:后端写数据库

solution 表中有一个 result 字段,表示用户提交状态,是一个枚举值,具体值如下:

#define OJ_WT0 0     // 提交排队
#define OJ_WT1 1     // 重判排队
#define OJ_CI 2      // 编译中(任务已派发)
#define OJ_RI 3      // 运行中
#define OJ_AC 4      // 答案正确
#define OJ_PE 5      // 格式错误
#define OJ_WA 6      // 答案错误
#define OJ_TL 7      // 时间超限
#define OJ_ML 8      // 内存超限
#define OJ_OL 9      // 输出超限
#define OJ_RE 10     // 运行错误
#define OJ_CE 11     // 编译错误
#define OJ_CO 12     // 编译完成
#define OJ_TR 13     // 测试运行结束
#define OJ_MC 14     // 等待裁判手工确认

当后端接收到用户在前端提交的数据后,将执行以下步骤:

  1. 初始化提交记录
    首先,将 problem_iduser_idlanguage 作为一条新记录插入 solution 表中(此时源码尚未存入)。同时,将该记录的 result 字段设置为 14,以标识该提交的源码尚未插入 source_code 表,避免判题机立即进行评测。插入完成后,数据库会自动生成该记录的主键 solution_id
  2. 存储源码并更新状态
    随后,后端会使用生成的 solution_id 和源码 source,将其作为一条新记录插入 source_code 表。插入成功后,solution 表中的 result 字段会被更新为 0,表示源码已成功存储,判题机可以开始评测该提交。

在做完这些工作后,后端需要 solution_id 返回给前端,前端此时需要使用solution_id 轮询后端提供的根据 solution_id 查询判题信息的接口。

第三步:judged 获取到提交

由于 judged 会轮询数据库,也就是每隔几秒执行一下这条 SQL:

SELECT * FROM solution WHERE result = 0;

judged 查询到 result = 0的记录后,就会将这条记录的 problem_idsolution_id告诉 judge_client,然后 judge_client就会准备判题了。

第四步:judge_client 开始判题(重点部分,先简略描述,后续系列会通过源码的方式十分详细的介绍)

在第三步中,judged 会用户的提交的 solution_idproblem_id 告诉 judge_client 后,judge_client 将执行以下步骤:

  1. 获取判题所需信息
    judge_client 根据 solution_idproblem_id 从数据库中查询详细信息,包括题目时间限制、空间限制、用户代码、提交语言等。
  2. 编译用户代码(解释型语言跳过这一步
    将用户的代码编译为对应的可执行文件,如果编译错误,则将错误信息与 solution_id 记录至 compileinfo 表,并将 solution 表中该提交的状态标记为“编译错误(CE)”,随后判题流程终止并返回结果。
  3. 创建沙箱环境
    根据获取的语言类型,创建对应的隔离沙箱环境,并在其中运行编译后的可执行文件,或直接执行无需编译的解释型语言代码。
  4. 运行用户代码
    用户代码在沙箱中运行,同时需要处理以下各种情况。
    1. 运行错误(RE):用户代码运行过程中异常终止(如数组越界、除零等错误)。此时需将错误信息存入 runtimeinfo 表,终止判题,并将 solution 表中的提交状态设为 “运行错误(Run Error)”。
    2. 输出超限(OLE):用户代码运行过程中生成的标准输出文件超出限制。此时需要终止判题,并将 solution 表中的提交状态设为 “输出超限(Output Limit Error)”。
    3. 运行超时(TLE,未运行完毕):代码尚未完成所有测试用例,但运行时间已超过最大时间限制。此时需终止判题,并将 solution 表中的提交状态设为 “时间超限(Time Limit Error)”。
    4. 内存超限(MLE,未运行完毕):代码尚未完成所有测试用例,但内存使用已超出最大内存限制。此时需终止判题,并将 solution 表中的提交状态设为 “内存超限(Memory Limit Error)”。
    5. 错误答案(WA):代码运行完所有测试用例后,与标准答案比对发现结果不匹配。需生成用户输出与标准答案的差异比对文件,并存入 runtimeinfo 表,并将 solution 表中的提交状态设为 “错误答案(Wrong Answer)”。
    6. 运行超时(TLE,运行完毕):代码已运行完所有测试用例,但运行时间超过题目设定的时间限制。此时需将 solution 表中的提交状态设为 “时间超限(Time Limit Error)”。
    7. 内存超限(MLE,运行完毕):代码已运行完所有测试用例,但内存使用超出题目设定的内存限制。此时需将 solution 表中的提交状态设为 “内存超限(Memory Limit Error)”。
    8. 格式错误(PE):代码输出与标准答案仅在空格、回车或其他空白字符上存在差异。此时需将 solution 表中的提交状态设为 “格式错误(Presentation Error)”。
    9. 答案正确(AC)。若以上情况都没出现,则将 solution 表中的提交状态设为 “答案正确(Accepted)”
  5. 销毁沙箱环境
    销毁沙箱,清除判题过程中生成的临时文件,进程退出。

注:判题机中存在两种时间限制——最大时间限制和题目时间限制。

  • 最大时间限制:用于防止判题机被恶意代码或用户写的死循环无限占用 CPU 资源。所以一旦用户代码运行时间超过该限制,系统将立即终止用户的代码运行。该限制通常由判题机统一设定,例如 10 秒或其他固定阈值。
  • 题目时间限制:针对特定题目设定,主要用于评估算法效率。即使用户提交的代码能产生正确结果,但若运行时间超出题目设定的时间限制,仍将被判定为超时(TLE)。该限制存储在 problem 表中,每道题可能有所不同。

同理,内存限制也分为全部最大限制和题目特定限制,具体规则与时间限制类似。

第五步:前端获取判题结果

当判题机将判题结果写入到数据库后,前端通过 solution_id 查询到判题结果后,将判题结果显示到浏览器界面上,整个判题流程到此完毕。

4. 数据库详细结构

4.1 problem 表详细结构

FieldTypeNullKeyDefaultExtra
problem_idintNOPRINULLauto_increment
titlevarchar(200)NO
descriptiontextYESNULL
inputtextYESNULL
outputtextYESNULL
sample_inputtextYESNULL
sample_outputtextYESNULL
spjchar(1)NO0
hinttextYESNULL
sourcevarchar(100)YESNULL
in_datedatetimeYESNULL
time_limitdecimal(10,3)NO0.000
memory_limitintNO0
defunctchar(1)NON
acceptedintYES0
submitintYES0
solvedintYES0
remote_ojvarchar(16)YESNULL
remote_idvarchar(32)YESNULL

字段解释:

  • problem_id: 主键,自增,用于唯一标识题目。
  • title: 题目标题。
  • description / input / output: 题目详细描述、输入和输出规范的文本。
  • sample_input / sample_output: 示例输入输出,用于帮助用户理解题目。
  • spj: 是否采用 Special Judge(特判),0 表示否,1 表示是特殊裁判,2 表示是文本裁判。
  • hint: 提示信息,可选,用于给出解题提示。
  • source: 题目来源或出处信息,如比赛名称或网站链接。
  • in_date: 题目录入系统的时间。
  • time_limit / memory_limit: 运行时间限制(单位秒)和内存限制
  • defunct: 是否下架该题目,N 表示正常显示,Y 表示不再显示在前台。
  • accepted / submit / solved: accepted:该题目被所有用户解题成功的次数,submit:该题目的提交总次数,solved:解出该题目的用户数量
  • remote_oj / remote_id: 用于支持远程判题(如同 LeetCode、Codeforces 等远程接口)的字段。记录远程 OJ 的名称和题目编号。

4.2 solution 表

FieldTypeNullKeyDefaultExtra
solution_idint unsignedNOPRINULLauto_increment
problem_idintNOMUL0
user_idchar(48)NOMULNULL
nickchar(20)NO
timeintNO0
memoryintNO0
in_datedatetimeNO2016-05-13 19:24:00
resultsmallintNOMUL0
languageint unsignedNO0
ipchar(46)NONULL
contest_idintYESMUL0
validtinyintNO1
numtinyintNO-1
code_lengthintNO0
judgetimetimestampYESCURRENT_TIMESTAMPDEFAULT_GENERATED
pass_ratedecimal(4,3) unsignedNO0.000
lint_errorint unsignedNO0
judgerchar(16)NOLOCAL
remote_ojchar(16)NO
remote_idchar(32)NO

字段解释:

  • solution_id: 提交记录主键,自增。
  • problem_id:problem 表的 problem_id 关联。表示该提交针对哪道题目。
  • user_id: 提交者的用户 ID,可与用户表进行关联(此处未展示用户表)。
  • nick: 提交者的昵称,用于在榜单或判题记录中显示。
  • time / memory: 程序运行时所耗费的 CPU 时间(单位毫秒或秒,视系统而定)和内存使用量。
  • in_date: 提交时间。
  • result: 判题结果,枚举值,见上文。
  • language: 提交所使用的编程语言代码(如 0 = C, 1 = C++, 2 = Java 等)。
  • ip: 提交者的 IP 地址。
  • contest_id: 如果是比赛期间的提交,可存储对应比赛的 ID;非比赛情况下可为默认值 0。
  • valid: 用于标识该提交记录是否有效,例如在判题故障时可能会设置为无效。
  • num: 在比赛场景中,一道题在某场比赛中的编号可能与系统默认的 problem_id 不同,可通过此字段记录。
  • code_length: 提交的源代码长度(单位字节)。
  • judgetime: 判题时间戳,记录判题所用时间,也可在某些业务场景用于分析统计。
  • pass_rate: 测试点通过率,若题目有多个测试点则可以记录通过的比例。
  • lint_error: 代码静态检测或风格检测的错误数(如 Clang-Tidy、ESLint 等)。
  • judger: 判题机的名称或标识,用于分布式判题时区分不同判题服务器。
  • remote_oj / remote_id: 远程判题平台及对应的提交 ID。

4.3 source_code 表

FieldTypeNullKeyDefaultExtra
solution_idintNOPRINULL
sourcetextNONULL
  • solution_id:solution 表的主键相对应,表示该源代码属于哪个提交。
  • source: 保存提交的完整源代码文本。由于可能非常长,使用 text 类型存储。

4.4 compileinfo 表

FieldTypeNullKeyDefaultExtra
solution_idintNOPRI0
errortextYESNULL
  • solution_id:solution 表的主键相对应,一对一关联。
  • error: 如果编译失败,此字段记录编译器返回的错误或警告信息,便于用户查看具体原因,例如 C 语言的中文分号等。

4.5 runtimeinfo 表

FieldTypeNullKeyDefaultExtra
solution_idintNOPRI0
errortextYESNULL
  • solution_id:solution 表的主键相对应,一对一关联。
  • error: 如果程序运行出错(如数组越界、除零等错误),或者在用户答案错误后的标准答案和用户答案的差异比对文件等。
http://www.dtcms.com/wzjs/458746.html

相关文章:

  • 嘉兴微网站建设可以推广的软件有哪些
  • 网站美工要求南宁网站推广大全
  • dede资讯类网站模板seo月薪
  • 沈阳做网站哪家便宜做电商必备的几个软件
  • app界面设计欣赏网站社群运营的经典案例
  • 互联网网站开发创业计划书拓客引流推广
  • 装修材料网购平台排名杭州网站优化企业
  • 沈阳专业网站建设公司网推广公司
  • 信息公司网站建设方案 游戏参考消息今天新闻
  • wordpress搞笑百度地图优化排名方法
  • 网站建设报销属于什么会计科目微营销推广方案
  • 微信推广小程序seo工程师
  • 网站开发gxjzdrj百度推广有用吗
  • 怎么检查网站死链代发新闻稿最大平台
  • wordpress的主题mnews1.9优化软件
  • 互联网行业五行属什么长沙网站seo优化排名
  • 郑州效果图设计广州seo工程师
  • 网站制作做站长挣钱vivo应用商店
  • 化妆品网站建设操作可行性分析网站优化方案案例
  • 淘宝做导航网站好郑州seo优化哪家好
  • 网站建设 预付款网络营销的类型
  • 自己做培训需要网站吗英文seo是什么意思
  • 基本网站建设技术柳州网站建设哪里有
  • 佛山新网站建设市场湖南好搜公司seo
  • 银川微信网站看网站时的关键词
  • 关于做情侣的网站的图片大全淘宝推广工具
  • 乐山企业品牌网站建设网站怎么收录到百度
  • 百度网站的域名是什么中国搜索引擎排行榜
  • 简述网站建设方法百度推广怎么操作
  • 个人网站建设方法和过程天津百度爱采购