JobObject实现对进程进行内存和运行时间限制
最近在研究oj系统,查过网上的解决方案,大致分为两种:
一种是基于Java虚拟机的解决方案,让编译好的程序运行在java虚拟机里面,通过对虚拟机的限制保障时空有效性和系统安全性;
第二种是基于linux系统的方案,通过内置的系统函数设置程序可用资源以及通过限制用户运行此程序以保障系统安全性.
实际情况是希望在windows下实现oj系统,而且对jvm无爱,遂研究下通过windows的一些内核机制实现此目的.
根据上面的第二种解决方案后查阅MSDN及相关资料后得知windows下没有设置程序可用资源的函数,只有通过JobObject创建一个工作对象以限制其运行.关于系统安全性方面则可以采用其他低权限账户运行(暂时没有找到更好的解决方案,沙箱或许可行,但是工程过于庞大.),这里不做过多介绍,详见CreateProcessAsUser,后续博文里可能会更详细的介绍.
接下来进入正题,通过JobObject创建新的工作对象大致经过如下步骤:
- 通过CreateJobObject创建一个工作对象;
- 通过SetInformationJobObject设置工作对象的参数,详见MSDN;
- 以CREATE_SUSPENDED方式启动进程,通过CreateProcessAsUser或者CreateProcess函数,可能还有其他函数,未做研究.
- 通过AssignProcessToJobObject将工作对象应用到指定进程中.
- 通过ResumeThread恢复已暂停的进程.
- 通过WaitForSingleObject等待正在运行的工作对象,设置好允许使用时间.
- 通过QueryInformationJobObject获取工作对象的最终状态.运行程序的退出状态可以通过GetExitCodeProcess获取.
- 扫尾工作(必须使用TerminateJobObject结束当前的工作对象,因为工作对象即便设置了PerProcessUserTimeLimit也无法使程序在超时后退出,没有研究原因).
以下是一段Demo代码:
#define _WIN32_WINNT 0x0500
#include "stdafx.h"
#include "windows.h"
#include "winbase.h"
int main(int argc, char* argv[])
{
LPCWSTR path=L"C:\\Time.exe";
HANDLE Job=CreateJobObject(NULL,NULL);
if(Job!=NULL)
{
printf("CreateJobObject [OK]\n");
JOBOBJECT_BASIC_LIMIT_INFORMATION Job_Limit;
memset(&Job_Limit,0,sizeof(Job_Limit));
Job_Limit.LimitFlags=JOB_OBJECT_LIMIT_WORKINGSET|JOB_OBJECT_LIMIT_PROCESS_TIME;
Job_Limit.MinimumWorkingSetSize=1;
Job_Limit.MaximumWorkingSetSize=1024*1024*10;//单位比特
Job_Limit.PerProcessUserTimeLimit.QuadPart=10000*1000*1;//单位100纳秒
if(SetInformationJobObject(Job,JobObjectBasicLimitInformation,&Job_Limit,sizeof(Job_Limit)))
{
printf("SetInformationJobObject [OK]\n");
STARTUPINFO pStaus = {sizeof(pStaus)};
PROCESS_INFORMATION pInfo;
if(CreateProcess(path,NULL,NULL,NULL,FALSE,CREATE_NEW_CONSOLE | CREATE_SUSPENDED,NULL,NULL,&pStaus,&pInfo))
{
printf("CreateProcess [OK]\n");
if(AssignProcessToJobObject(Job,pInfo.hProcess))
{
printf("AssignProcessToJobObject [OK]\n");
ResumeThread(pInfo.hThread);
CloseHandle(pInfo.hThread);
DWORD WaitRe = WaitForSingleObject(Job,1000);
if(WaitRe!=WAIT_FAILED)
{
printf("WaitForSingleObject [OK]\n");
JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo;
DWORD lpReturnLength;
if(QueryInformationJobObject(Job,JobObjectExtendedLimitInformation,&lpJobObjectInfo,sizeof(lpJobObjectInfo),&lpReturnLength))
{
printf("Memory:%dKb\n",lpJobObjectInfo.PeakProcessMemoryUsed/1024);
DWORD ExitCode=0;
//TerminateJobObject(Job,0);
if(GetExitCodeProcess(pInfo.hProcess,&ExitCode))
{
printf("ExitCode:%d\n",ExitCode);
}
}
}
else
{
printf("WaitForSingleObject [Error:%d]\n",GetLastError());
}
TerminateJobObject(Job,0);//exit
CloseHandle(pInfo.hProcess);
CloseHandle(Job);
}
else
{
TerminateProcess(pInfo.hProcess,0);//Change
printf("AssignProcessToJobObject [Error:%d]\n",GetLastError());
}
}
else
{
printf("CreateProcess [Error:%d]\n",GetLastError());
}
}
else
{
printf("SetInformationJobObject [Error:%d]\n",GetLastError());
}
}
else
{
printf("CreateJobObject [Error:%d]\n",GetLastError());
}
system("pause");
return 0;
}
最近因为CreateProcessAsUser和CreateProcessWithLogonW头疼很久,首先logon版本似乎在XP以后,第一个登录的session账户自动会套用匿名job中使得无法把进程套用到自己的job中(WIN7倒是有方案绕过)
之后用AsUser版本必须要求具备服务级别的权限,然后折腾了很久创建出来的程序在受限账户下没有办法套用默认的皮肤,很想讨教一下,感谢先。
http://acm.guetonline.com/problemset
这是我做的Windows下的OJ。。方法基本和这个一致。。
我写的这个还只是个皮毛,还请多多指教哈.
hi.
我看到http://www.hostloc.com/thread-102375-1-1.html,你买了个techievps,我也买了个。你能在它上面自己安装win os吗?(不用他的solusvm panel里的os 列表里的win os,那个win os 好像有问题。
那个上传镜像的工具貌似有问题,发过tk,听他们的意思是不支持使用自定义的镜像.
我也在做OJ,不过暂时不做评测程序。
以后要向你请教哦
谈不上请教,我也是瞎折腾