OJInt32_t WindowsProcess::create(const OJString &cmd, const OJInt32_t timeLimit, const OJInt32_t memoryLimit, bool startImmediately) { ILogger *logger = LoggerFactory::getLogger(LoggerId::AppInitLoggerId); { logger->logTraceX(OJStr("[process] - create - CMD='%s' T=%dms, M=%dbytes"), cmd.c_str(), timeLimit, memoryLimit); } if(!createInputFile()) { logger->logError(OJStr("[process] - create - can't creat inputFile")); return -1; } if(!createOutputFile()) { logger->logError(OJStr("[process] - create - can't creat outputFile")); return -1; } if (!jobHandle_.create(useToExcuter_)) { logger->logError(OJStr("[process] - create - can't creat job")); return -1; } if (!jobHandle_.setLimit(timeLimit, memoryLimit)) { logger->logError(OJStr("[process] - create - set job limit failed")); return -1; } OJChar_t cmdline[1024]; wcscpy_s(cmdline, cmd.c_str()); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); si.cb = sizeof(si); si.wShowWindow = SW_HIDE;//隐藏窗口 si.hStdInput = inputFileHandle_; si.hStdOutput = si.hStdError = outputFileHandle_; si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; //使用handel项和wShowWindow项。 DWORD createFlag = CREATE_SUSPENDED | CREATE_NO_WINDOW | CREATE_BREAKAWAY_FROM_JOB; bool res = createProcess( NULL, // No module name (use command line). cmdline, // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. TRUE, // Set handle inheritance to ... createFlag, // creation flags. NULL, // Use parent 's environment block. NULL, // Use parent 's starting directory. &si, // Pointer to STARTUPINFO structure. &pi); // Pointer to PROCESS_INFORMAT\ION structure. if (!res) { logger->logErrorX(OJStr("[process] - can't creat process. last error: %u"), GetLastError()); return -1; } alive_ = true; processHandle_ = pi.hProcess; threadHandle_ = pi.hThread; if(startImmediately) return start(); return 1; }
OJInt32_t WindowsProcess::start() { ILogger *logger = LoggerFactory::getLogger(LoggerId::AppInitLoggerId); //加到作业中 if (!jobHandle_.assinProcess(processHandle_)) { logger->logErrorX(OJStr("[process] - can't assign process to job! error:%u"), GetLastError()); kill(); return -1; } //启动线程 ResumeThread(threadHandle_); //关闭不使用的句柄。让进程执行完毕后立即退出。 SAFE_CLOSE_HANDLE_AND_RESET(threadHandle_); SAFE_CLOSE_HANDLE_AND_RESET(inputFileHandle_) SAFE_CLOSE_HANDLE_AND_RESET(outputFileHandle_) result_ = AppConfig::JudgeCode::Accept; DWORD ExecuteResult = -1; ULONG completeKey; LPOVERLAPPED processInfo; bool done = false; while(!done) { if(!jobHandle_.getState(ExecuteResult, completeKey, processInfo)) { DEBUG_MSG(OJStr("get job State faild!")); OJSleep(1); continue; } DWORD dwCode = (DWORD)processInfo; switch (ExecuteResult) { case JOB_OBJECT_MSG_NEW_PROCESS: //DEBUG_MSG_VS(OJStr("[WindowsProcess]new process: %u"), dwCode); break; case JOB_OBJECT_MSG_END_OF_JOB_TIME: //job超时 DEBUG_MSG(OJStr("[WindowsProcess]Job time limit reached")); result_ = AppConfig::JudgeCode::TimeLimitExceed; done = true; break; case JOB_OBJECT_MSG_END_OF_PROCESS_TIME: //线程超时 DEBUG_MSG(OJStr("[WindowsProcess]process time limit reached")); result_ = AppConfig::JudgeCode::TimeLimitExceed; done = true; break; case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: //进程内存超限 DEBUG_MSG(OJStr("[WindowsProcess]Process exceeded memory limit")); result_ = AppConfig::JudgeCode::MemoryLimitExceed; done = true; break; case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT: //job内存超限 { OJInt32_t mem = getRunMemory(); DebugMessage(OJStr("[WindowsProcess]exceeded job memory limit with %dkb"), mem); result_ = AppConfig::JudgeCode::MemoryLimitExceed; done = true; } break; case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: //超出运行的进程数量 DEBUG_MSG(OJStr("[WindowsProcess]Too many active processes in job")); result_ = AppConfig::JudgeCode::RuntimeError; done = true; break; case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: DEBUG_MSG(OJStr("[WindowsProcess]Job contains no active processes")); done = true; break; case JOB_OBJECT_MSG_EXIT_PROCESS: //进程退出 //DEBUG_MSG_VS(OJStr("[WindowsProcess]Process %u exit."), dwCode); if(::GetProcessId(processHandle_) == dwCode) { done = true; } break; case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: //进程异常结束 DEBUG_MSG(OJStr("[WindowsProcess]Process terminated abnormally")); result_ = AppConfig::JudgeCode::RuntimeError; done = true; break; default: DEBUG_MSG(OJStr("[WindowsProcess]Unknown notification")); result_ = AppConfig::JudgeCode::UnknownError; break; } } { FILETIME ftime, temp; ::GetProcessTimes(processHandle_, &temp, &temp, &temp, &ftime); ULARGE_INTEGER time2; time2.LowPart = ftime.dwLowDateTime; time2.HighPart = ftime.dwHighDateTime; runTime_ = time2.QuadPart / 10000; } { PROCESS_MEMORY_COUNTERS info; ::GetProcessMemoryInfo(processHandle_, &info, sizeof(info)); runMemory_ = info.PeakPagefileUsage; } while(!jobHandle_.terminate())//强制关闭作业 { DEBUG_MSG(OJStr("Terminate job faild!")); OJSleep(10); } alive_ = false;//进程结束 //正常退出。即不是超时等状况。 if(result_ == AppConfig::JudgeCode::Accept) { DWORD code = getExitCode();//获取进程返回值,以判断进程是否执行成功。 if(code != 0) { result_ = AppConfig::JudgeCode::RuntimeError; DEBUG_MSG_VS(OJStr("process exit with code : %u, last error: %u"), code, GetLastError()); } } SAFE_CLOSE_HANDLE_AND_RESET(processHandle_); return 0; }
bool WindowsJob::setLimit(const OJInt32_t timeLimit, const OJInt32_t memoryLimit) { ILogger *logger = LoggerFactory::getLogger(LoggerId::AppInitLoggerId); //限制时间,单位为100ns。 1ms = 10的6次方ns = 10000 * 100ns。 OJInt64_t limitTime = timeLimit * 10000; // ms->100ns int limitMemory = memoryLimit; //bytes if (limitMemory <= 0) //超出int范围了 limitMemory = 128 * 1024 * 1024; //默认128M //设置基本限制信息 JOBOBJECT_EXTENDED_LIMIT_INFORMATION subProcessLimitRes; ZeroMemory(&subProcessLimitRes, sizeof(subProcessLimitRes)); JOBOBJECT_BASIC_LIMIT_INFORMATION & basicInfo = subProcessLimitRes.BasicLimitInformation; basicInfo.LimitFlags = \ JOB_OBJECT_LIMIT_PRIORITY_CLASS | /*限制job优先级*/ \ JOB_OBJECT_LIMIT_PROCESS_TIME | /*限制job时间*/ \ JOB_OBJECT_LIMIT_PROCESS_MEMORY | /*限制job内存*/ \ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | /*遇到异常,让进程直接死掉。*/\ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | /*进程跟随job一起关闭*/\ JOB_OBJECT_LIMIT_BREAKAWAY_OK; if(useToExcuter_) { basicInfo.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; basicInfo.ActiveProcessLimit = 1; } basicInfo.PriorityClass = NORMAL_PRIORITY_CLASS; //优先级为默认 basicInfo.PerProcessUserTimeLimit.QuadPart = limitTime; subProcessLimitRes.ProcessMemoryLimit = limitMemory; if (!setInformation(JobObjectExtendedLimitInformation, &subProcessLimitRes, sizeof(subProcessLimitRes))) { logger->logErrorX(OJStr("[process] - setLimit - can't set job extend info! error:%u"), GetLastError()); return false; } //让完成端口发出时间限制的消息 JOBOBJECT_END_OF_JOB_TIME_INFORMATION timeReport; ZeroMemory(&timeReport, sizeof(timeReport)); timeReport.EndOfJobTimeAction = JOB_OBJECT_POST_AT_END_OF_JOB;//时间到了,通过管道发出信息。 if (!setInformation(JobObjectEndOfJobTimeInformation, &timeReport, sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION))) { logger->logErrorX(OJStr("[process] - setLimit - can't set job end info! error:%u"), GetLastError()); return false; } //UI限制。禁止访问一些资源。 JOBOBJECT_BASIC_UI_RESTRICTIONS subProcessLimitUi; ZeroMemory(&subProcessLimitUi, sizeof(subProcessLimitUi)); subProcessLimitUi.UIRestrictionsClass = JOB_OBJECT_UILIMIT_ALL; if (!setInformation(JobObjectBasicUIRestrictions, &subProcessLimitUi, sizeof(subProcessLimitUi))) { logger->logErrorX(OJStr("[process] - setLimit - can't set job limit info! error:%u"), GetLastError()); return false; } //将作业关联到完成端口,以确定其运行情况,及退出的原因。完成端口可理解为管道,job和应用程序分别位于管道的两端。 //应用程序可以通过管道,查询job的工作状态。 s_mutex_.lock(); ULONG id = ++s_id_; s_mutex_.unlock(); //创建完成端口 iocpHandle_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, id, 0); if (NULL == iocpHandle_) { logger->logErrorX(OJStr("[process] - setLimit - create IOCP failed! error:%u"), GetLastError()); return false; } JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobCP; ZeroMemory(&jobCP, sizeof(jobCP)); jobCP.CompletionKey = (PVOID)id; jobCP.CompletionPort = iocpHandle_; if (!setInformation(JobObjectAssociateCompletionPortInformation, &jobCP, sizeof(jobCP))) { logger->logErrorX(OJStr("[process] - setLimit - can't set job CompletionPort info! error:%d"), GetLastError()); return false; } return true; }