zoukankan      html  css  js  c++  java
  • HUSTOJ的Windows版评判内核(限制内存使用)

    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的瞬间,子进程就弹出错误对话框了。如:

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

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

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

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

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

  • 相关阅读:
    .gitignore规则不生效的解决办法
    docker使用
    mysql 操作
    outlook转发问题
    我的梦想
    安静与流动
    sql 统计 学生成绩2
    sql 统计 关于学生成绩
    数据库备份
    web 注销回到登录页面
  • 原文地址:https://www.cnblogs.com/findumars/p/6769907.html
Copyright © 2011-2022 走看看