HUSTOJ的Windows版评判内核(一)

作者:游蓝海

个人主页:http://blog.csdn.net/you_lan_hai

2013.4.9 注:最新版本项目地址:https://github.com/NsLib/FreeJudger。新版FreeJudger,跟我之前写的这个已经完全不一样了,之前的这个废除。虽然现在工作忙,但我们会继续开发FreeJudger,直到功能完善,详见:HUSTOJ的Windows版评判内核(二)

在线评测系统(Online Judge System,OJ),即在线评判用户所提交的代码是否能够解决相应的题目,常作为高校训练学生编程实践能力的平台已及ACM/ICPC等程序设计竞赛平台。目前,985、211类的实力高校,基本都拥有自己的OJ,而对于实力较弱的高校来说,开发一个OJ是相当困难的一件事。虽然OJ系统可以共享,但是如果作为内部练习和训练,依赖别人的oj,并不是很方便。如今,OJ系统已经非常多了,但是开源的系统却并不多,而且开源的评判内核就更少了,如果评判程序出了问题,还得等别人出更新。这也许是因为开发过程比较辛苦的原因吧。

HUSTOJ是一个出色的开源的系统,它遵循GPL,不仅功能齐全,而且还有开发团队维护,免去了高校的重复劳动力(详见:http://code.google.com/p/hustoj/)。虽然web可以搭建到任何一款操作系统上,但是HUSTOJ的评判内核却是linux版的,我相信用windows的菜鸟团队会更多,至少我们当年就是这样的,要搭建一个linux环境何其困难呀。

以前在学校的时候,我是ACM团队的负责人之一,我们没有自己的OJ,训练的时候都会去其他学校的OJ做题。大部分学校OJ的题库偏难,挫败了无数的新手,我目睹几百人的团队,后来流失到30多人,目睹了很多想成为编程高手的计算机系学弟去打游戏了,作为一个团队负责人,我感觉我失职了。虽然有部分学校的OJ题会简单些,但是我们经常会遇到这样的尴尬,重要时刻网站无法登录!

作为苦逼的无人问津院校的最差专业的想为自己开发团队打造自主oj的你,加入我们吧:群117975329,验证信息CSDN。

好了,废话不多说,无码无真相,windows版HUSTOJ评判内核项目:http://code.google.com/p/online-judger/ ,目前已经完成大部分功能,基本可用,建议用svn工具(推荐TortoiseSVN)下载最新源码。

原理说明
        1.数据库管理模块(DBManager)从数据库查询出当前结果为待定和等待重判的提交信息,并组装成内部可识别任务数据(Task),然后将Task转交给评判单元(JudgeCell),等待评判。
        2.评判单元(JudgeCell)管理着一组评判内核(JudgeCore),每个评判内核(JudgeCore)都运行在独立的线程中。评判内核不断的从评判单元中获取任务,如果发现任务就进行一次评判,然后通过回调接口,将评判结果反馈给上层,最终,评判的结果会反馈给DBManager。
        3.DBManager收到结果后,将结果写回数据库。
        4.一个评判内核(JudgeCore)由三个执行部件(Excuter)组成,分别是编译器(Compiler)、执行器(Runner)、匹配器(Matcher)组成,分别负责编译用户代码,执行用户程序并生成输出数据,匹配用户程序的输出数据是否与测试数据匹配。JudgeCore目前只支持c和c++两种语言。


开发工具

visual studio 2008

解决方案构成
        解决方案共有3个项目,LZData,acm,Judger。
        1.LZData,是我其他项目中读/写配置文件的工具,目前支持LZD和XML两种格式。对XML格式的支持不是很完善,目前只支持赋值类语法,不支持注释、帮助等格式的语法。
        2.acm,简单的封装了一些常用的windows API,如线程、进程、网络通信、文件处理、MySql等。
        3.Judger,是HUSTOJ的评判内核程序。Judger/bin是内核程序的输出目录。

TODO
1.支持评判内核沙箱运行模式。
2.增加对Java代码评判的支持。
3.增加对sim的支持。
4.评判单元多进程化。
5.HUSTOJ IDE测试功能的支持。

简易测试环境搭建

1.安装wamp(windows+apache+MySQL+php)集成环境。

2.下载HUSTOJ,将其web目录放置到wamp/www目录下。数据库的配置参考hustoj/install/readme。

3.下载本windows版评判程序,编译后,在judger/bin目录下生成judger.exe。judger.exe可以放置到任意目录,但是配置文件(config.xml)中的<testDataPath>项路径必须与hustoj 测试数据的配置项‘OJ_DATA’保持一致,此路径最好都用绝对路径。

程序写的不是很好,欢迎各位windows编程砖家以及oj内核砖家留下您宝贵的一砖。其中在编写windows沙箱(job)功能时遇到了几个棘手的问题:

1.job时间已经到了,程序还未终止,往往要多等待2-5s。

2.评判含有静态声明的大数组(数组内存超出job的限制内存)代码时,在进程添加到job之后,调用ResumeThread的瞬间,子进程就弹出错误对话框了。如:

  1. #include .....
  2. char buffer[100*1024*1024];//超出job限制内存。
  3. int main().....

因此,程序执行程序没有使用到沙箱功能,而是简单的启动监视线程来监视子进程的执行情况,如果子进程超出限制,则强行结束。

附上一段沙箱(job)实现代码,望高手指点:

    1. bool ZProcessJob::create(const tstring &  cmd, bool start_/* = true*/)
    2. {
    3. if (NULL != m_hProcess)
    4. {
    5. OutputMsg(_T("process has been created!"));
    6. return false;
    7. }
    8. int64 limitTime = m_limitTime * 10000; //100ns (1s = 10^9ns)
    9. int limitMemory = m_limitMemory * 1024; //bytes
    10. if (limitMemory < 0)//超出int范围了
    11. {
    12. limitMemory = 128*1024*1024; //默认128M
    13. }
    14. //////////////////////////////////////////////////////////////////////////
    15. //创建作业沙箱(job)
    16. //////////////////////////////////////////////////////////////////////////
    17. tstring jobName;
    18. generateGUID(jobName);
    19. if(!m_job.create(jobName))
    20. {
    21. OutputMsg(_T("create job faild!"));
    22. return false;
    23. }
    24. //////////////////////////////////////////////////////////////////////////
    25. //设置job信息
    26. //////////////////////////////////////////////////////////////////////////
    27. //设置基本限制信息
    28. JOBOBJECT_EXTENDED_LIMIT_INFORMATION subProcessLimitRes;
    29. ZeroMemory(&subProcessLimitRes, sizeof(subProcessLimitRes));
    30. JOBOBJECT_BASIC_LIMIT_INFORMATION & basicInfo = subProcessLimitRes.BasicLimitInformation;
    31. basicInfo.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_TIME| \
    32. JOB_OBJECT_LIMIT_PRIORITY_CLASS| \
    33. JOB_OBJECT_LIMIT_PROCESS_MEMORY| \
    34. JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
    35. basicInfo.PriorityClass = NORMAL_PRIORITY_CLASS;      //优先级为默认
    36. basicInfo.PerProcessUserTimeLimit.QuadPart = limitTime; //子进程执行时间ns(1s=10^9ns)
    37. subProcessLimitRes.ProcessMemoryLimit = limitMemory;    //内存限制
    38. m_job.setInformation(
    39. JobObjectExtendedLimitInformation,
    40. &subProcessLimitRes,
    41. sizeof(subProcessLimitRes));
    42. //让完成端口发出时间限制的消息
    43. JOBOBJECT_END_OF_JOB_TIME_INFORMATION timeReport;
    44. ZeroMemory(&timeReport, sizeof(timeReport));
    45. timeReport.EndOfJobTimeAction = JOB_OBJECT_POST_AT_END_OF_JOB;
    46. m_job.setInformation(
    47. JobObjectEndOfJobTimeInformation,
    48. &timeReport,
    49. sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION));
    50. //UI限制
    51. JOBOBJECT_BASIC_UI_RESTRICTIONS subProcessLimitUi;
    52. ZeroMemory(&subProcessLimitUi, sizeof(subProcessLimitUi));
    53. subProcessLimitUi.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE| \
    54. JOB_OBJECT_UILIMIT_DESKTOP| \
    55. JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS| \
    56. JOB_OBJECT_UILIMIT_DISPLAYSETTINGS| \
    57. JOB_OBJECT_UILIMIT_EXITWINDOWS| \
    58. JOB_OBJECT_UILIMIT_GLOBALATOMS| \
    59. JOB_OBJECT_UILIMIT_HANDLES| \
    60. JOB_OBJECT_UILIMIT_READCLIPBOARD;
    61. m_job.setInformation(
    62. JobObjectBasicUIRestrictions,
    63. &subProcessLimitUi,
    64. sizeof(subProcessLimitUi));
    65. //将作业关联到完成端口,以确定其运行情况,及退出的原因
    66. int id = generateID();
    67. m_ioCPHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, id, 0);
    68. JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobCP;
    69. ZeroMemory(&jobCP, sizeof(jobCP));
    70. jobCP.CompletionKey = (PVOID)id;
    71. jobCP.CompletionPort = m_ioCPHandle;
    72. m_job.setInformation(
    73. JobObjectAssociateCompletionPortInformation,
    74. &jobCP,
    75. sizeof(jobCP));
    76. //////////////////////////////////////////////////////////////////////////
    77. //创建子进程
    78. //////////////////////////////////////////////////////////////////////////
    79. TCHAR cmd_[BUFSIZE];
    80. lstrcpy(cmd_, cmd.c_str());
    81. m_hInput = createInputFile();
    82. m_hOutput = createOutputFile();
    83. /*CreateProcess的第一个参数
    84. 将标准输出和错误输出定向到我们建立的m_hOutput上
    85. 将标准输入定向到我们建立的m_hInput上
    86. 设置子进程接受StdIn以及StdOut的重定向
    87. */
    88. STARTUPINFO StartupInfo;
    89. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    90. StartupInfo.cb = sizeof(STARTUPINFO);
    91. StartupInfo.hStdOutput = m_hOutput;
    92. StartupInfo.hStdError = m_hOutput;
    93. StartupInfo.hStdInput = m_hInput;
    94. StartupInfo.dwFlags = STARTF_USESTDHANDLES;
    95. PROCESS_INFORMATION ProcessInfo;
    96. ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
    97. if(!createProcess(cmd_, TRUE, CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
    98. StartupInfo, ProcessInfo))
    99. {
    100. return false;
    101. }
    102. m_hProcess = ProcessInfo.hProcess;
    103. m_hThread = ProcessInfo.hThread;
    104. //////////////////////////////////////////////////////////////////////////
    105. //将子进程与job关联
    106. //////////////////////////////////////////////////////////////////////////
    107. if (start_)
    108. {
    109. start();
    110. }
    111. return true;
    112. }
    113. bool ZProcessJob::start()
    114. {
    115. OutputMsgA("start run.");
    116. if(!m_job.assinProcess(m_hProcess))
    117. {
    118. OutputMsg(_T("应用进程到job失败!%d"), GetLastError());
    119. return false;
    120. }
    121. //启动子进程
    122. ResumeThread(m_hThread);
    123. //关闭标准输入文件和零时输出文件的句柄
    124. SAFE_CLOSE_HANDLE(m_hInput);
    125. SAFE_CLOSE_HANDLE(m_hOutput);
    126. //关闭主进程主线程句柄
    127. SAFE_CLOSE_HANDLE(m_hThread);
    128. //等待进程子进程处理完毕或耗尽资源退出
    129. DWORD ExecuteResult = -1;
    130. unsigned long completeKey;
    131. LPOVERLAPPED processInfo;
    132. bool done = false;
    133. while(!done)
    134. {
    135. GetQueuedCompletionStatus(
    136. m_ioCPHandle,
    137. &ExecuteResult,
    138. &completeKey,
    139. &processInfo,
    140. INFINITE);
    141. switch (ExecuteResult)
    142. {
    143. case JOB_OBJECT_MSG_NEW_PROCESS:
    144. {
    145. OutputMsg(TEXT("New process (Id=%d) in Job"), processInfo);
    146. }
    147. break;
    148. case JOB_OBJECT_MSG_END_OF_JOB_TIME:
    149. {
    150. OutputMsg(TEXT("Job time limit reached"));
    151. m_exitCode = 1;
    152. done = true;
    153. }
    154. break;
    155. case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
    156. {
    157. OutputMsg(TEXT("Job process (Id=%d) time limit reached"), processInfo);
    158. m_exitCode = 1;
    159. done = true;
    160. }
    161. break;
    162. case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:
    163. {
    164. OutputMsg(TEXT("Process (Id=%d) exceeded memory limit"), processInfo);
    165. m_exitCode = 2;
    166. done = true;
    167. }
    168. break;
    169. case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:
    170. {
    171. OutputMsg(TEXT("Process (Id=%d) exceeded job memory limit"), processInfo);
    172. m_exitCode = 2;
    173. done = true;
    174. }
    175. break;
    176. case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
    177. {
    178. OutputMsg(TEXT("Too many active processes in job"));
    179. }
    180. break;
    181. case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
    182. {
    183. OutputMsg(TEXT("Job contains no active processes"));
    184. done = true;
    185. }
    186. break;
    187. case JOB_OBJECT_MSG_EXIT_PROCESS:
    188. {
    189. OutputMsg(TEXT("Process (Id=%d) terminated"), processInfo);
    190. done = true;
    191. }
    192. break;
    193. case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
    194. {
    195. OutputMsg(TEXT("Process (Id=%d) terminated abnormally"), processInfo);
    196. m_exitCode = 3;
    197. done = true;
    198. }
    199. break;
    200. default:
    201. OutputMsg(TEXT("Unknown notification: %d"), ExecuteResult);
    202. m_exitCode = 99;
    203. break;
    204. }
    205. }
    206. JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION jobai;
    207. ZeroMemory(&jobai, sizeof(jobai));
    208. QueryInformationJobObject(m_job.handle(), JobObjectBasicAndIoAccountingInformation,
    209. &jobai, sizeof(jobai), NULL);
    210. JOBOBJECT_EXTENDED_LIMIT_INFORMATION joeli;
    211. ZeroMemory(&joeli, sizeof(joeli));
    212. QueryInformationJobObject(m_job.handle(), JobObjectExtendedLimitInformation,
    213. &joeli, sizeof(joeli), NULL);
    214. m_runTime = jobai.BasicInfo.TotalUserTime.LowPart/10000;
    215. m_runMemory = joeli.PeakProcessMemoryUsed/1024;
    216. //关闭进程句柄
    217. SAFE_CLOSE_HANDLE(m_hProcess);
    218. //关闭完成端口
    219. SAFE_CLOSE_HANDLE(m_ioCPHandle);
    220. //为了安全,杀死作业内所有进程
    221. while(!m_job.terminate(0))
    222. {
    223. OutputMsg(_T("停止job失败!%d"), GetLastError());
    224. Sleep(1000);
    225. }
    226. //关闭作业句柄
    227. m_job.close();
    228. OutputMsgA("end run.");
    229. return true;
    230. }

http://blog.csdn.net/you_lan_hai/article/details/8521603/

HUSTOJ的Windows版评判内核(限制内存使用)的更多相关文章

  1. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  2. elasticsearch 2.4 windows版jvm内存设置

    本文编写目的是因为网上有很多es修改内存配置的文章,方法也各有不同,但在我的情况下(es 2.4 windows版)发现很多方法都是无效的,有效只有以下方法 第一个是xms,第二个是xmx

  3. .net core 使用windows版redis

    在项目中为了减少程序占用内存(将结果保存在全局变量里面,会占用内存),要求使用redis.开始了爬坑的过程.o(╥﹏╥)o c#操作redis 基本就这3中情况: ServiceStack.Redis ...

  4. Redis Windows版安装及简单使用

    1.Redis简介及优势 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. 特点: Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次 ...

  5. MS Open Tech 技术团队构建可靠的Windows版Redis

    可靠的Windows版Redis 副标题: 评论更精彩,教你怎么解决64位Windows版Redis狂占C盘的问题. MS Open Tech 技术团队最近花了很多时间来测试最新构建的Windows版 ...

  6. Redis 主从配置(Windows版)

    安装从库 1.复制一份 Redis 文件,当做从库. 2.修改从库文件中 redis.windows.conf 的端口号. 3.安装服务,需要重新设置名称.然后去服务中,开启“redis6380”(此 ...

  7. Zookeeper Windows版的服务安装和管理工具

    以前研究过负载均衡,最近正在项目上实施(从来没做过小项目以上级别的东西,哈).然后遇到了多个一模一样但是同时运行的服务.不同服务但依赖同相同的配置数据(前端网页服务:Nginx+IIS+nodejs. ...

  8. Redis&PHP的使用安装-windows版

    Redis是一个Key-value的数据结构存储系统,可以以数据库的形式,缓存系统,消息处理器使用,它支持的存储value类型很多,例如,string.list(链表).set(集合).zset(so ...

  9. 可靠的Windows版Redis

    副标题: 评论更精彩,教你怎么解决64位Windows版Redis狂占C盘的问题. MS Open Tech 技术团队近期花了非常多时间来測试最新构建的Windows版Redis(可在 MS Open ...

随机推荐

  1. erlang app 文件

    http://hje.iteye.com/blog/1211734 应用的概念¶ 当我们写了实现特定功能的代码之后,我们可能想将代码转成一个 应用 (application),这是可以作为一个单元启动 ...

  2. spring boot 生成 war 包有一个war.original是什么?

    两个坑 .war.original 生成这种格式的文件,是因为在开启了二次打包.具体可以看这里 修改入口文件的配置 , 官方文档看这里 类似下面的代码,要继承SpringBootServletInit ...

  3. WPF 插拔触摸设备触摸失效

    原文:WPF 插拔触摸设备触摸失效 最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效.通过分析 WPF 源代码可以找到 WPF 触摸失效的原因. 在 Windows 会将所有的 H ...

  4. Java网络编程注意事项1

    网络编程的基础知识 什么是计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备通信线路互连成一个规模大.功能强的网络系统. 计算机网络主要能做些下面功能: 1)资源共享 2)信息传输与集中处理 ...

  5. Troubleshooting routing topology based on a reference topology

    In one embodiment, a computing device (e.g., border router or network management server) transmits a ...

  6. 实战caffe多标签分类——汽车品牌与车辆外观(C++接口)[详细实现+数据集]

    前言 很多地方我们都需要用到多标签分类,比如一张图片,上面有只蓝猫,另一张图片上面有一只黄狗,那么我们要识别的时候,就可以采用多标签分类这一思想了.任务一是识别出这个到底是猫还是狗?(类型)任务二是识 ...

  7. UVA - 10312 Expression Bracketing

    Description Problem A Expression Bracketing Input: standard input Output: standard output Time Limit ...

  8. 绝对和相对误差(absolute & relative error)

    1. 标量 真实值为 x,测量值为 x0, 绝对误差(absolute error):Δx=x0−x(有单位): 相对误差(relative error):δx=Δxx=x0−xx=x0x−1(是一个 ...

  9. wpf 屏蔽热键

    原文:wpf 屏蔽热键 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a771948524/article/details/9428923   us ...

  10. hdoj 5087 Revenge of LIS II 【第二长单调递增子】

    称号:hdoj 5087 Revenge of LIS II 题意:非常easy,给你一个序列,让你求第二长单调递增子序列. 分析:事实上非常easy.不知道比赛的时候为什么那么多了判掉了. 我们用O ...