微软提供了一个作业内核对象,它能够使我们对一组进程进行一些限制,同时创建一个只包含一个进程的作业也非常有用,因为能对该进程添加平时不能施加的作用。
例如设计一个服务器时,也许客户端会请求服务器的一些应用程序处理一些数据并返回,因为许多的客户端都可能连接到这个服务器,所以服务器应该限制客户端能请求的东西,避免客户端的独占许多的资源。这些限制包括分配给客户端请求的最大CPU时间、最小工作集最大工作集大小、禁止客户端应用程序关闭计算机以及一些安全限制。
1.创建一个作业内核对象
HANDLE CreateJobObject(PSECURITY_ATTRIBUTES psa,PCTATR pszName);
psa:安全属性(大部分和是否可以进程间继承有关),默认安全属性填充NULL,详细见MSDN
pszName:给这个内核对象赋予的名字。
2.打开一个作业对象。
HANDLE OpenJobObject(DWORD dwDesiredAccess,BOOL bInheritHandle,PCSTR pszName);
dwDesiredAccess:有以下几个值可以填充:
MAXIMUM_ALLOWED:指定对调用方有效的作业对象的最大访问权限。
JOB_OBJECT_ASSIGN_PROCESS:允许将进程分配给作业。
JOB_OBJECT_SET_ATTRIBUTES:指定对象的集合属性访问权限。允许设置作业对象属性。
JOB_OBJECT_QUERY:指定对对象的查询访问权限。允许查询作业对象属性和会计信息。
JOB_OBJECT_TERMINATE:指定对象的终止访问权限。允许终止作业对象中的所有进程。
JOB_OBJECT_SET_SECURITY_ATTRIBUTES:指定对象的安全属性访问权限。允许设置作业对象中所有进程的安全限制。
JOB_OBJECT_ALL_ACCESS:指定对作业对象的完全访问权限。
bInheritHandle:作业对象句柄是否可以被新的子进程或者其他进程继承。
pszName:作业对象的名称,一般的,这个名称应该在系统中使独一无二的。
3.对作业集中的进程施加限制。
HANDLE SetInformationJobObject(HANDLE hJob,JOBOBJECTINFOCLASS JobOjectInformationClass,PVOID pJobObjectInformation,DWORD cbJobObjectInformationsize);
第二个参数和第三个参数是对应的,第二个参数指定了什么值第三个参数就会使用指定类型的结构体。
cbJobObjectInformationsize:传入第三个指定结构体的大小。
下边介绍这几个指定结构体的 常用 的成员。
JOBOBJECT_BASIC_LIMIT_INFORMATION:
typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION{
LARGE_INTEGER PerProcessUserTimeLimit;
LARGE_INTEGER PerJobUserTimeLimit;
DWORD LimitFlags;
DWORD MinimumWorkSetSize;
DWORD MaximumWorkSetSize;
DWORD ActiveProcessLimit;
DWORD_PTR Affinity;
DWORD PriorityClass;
DWORD SchedulingClass;
}JOBOBJECT_BASIC_LIMIT_INFORMATION,*PJOBOBJECT_BASIC_LIMIT_INFORMATION
JOBOBJECT_EXTENDED_LIMIT_INFORMATION
JOBOBJECT_EXTENDED_LIMIT_INFORMATION的结构如下:
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
IO_COUNTERS IoInfo;
SIZE_T ProcessMemoryLimit;
SIZE_T JobMemoryLimit;
SIZE_T PeakProcessMemoryLimitUsed;
SIZE_T PeakJobMemoryLimitUsed;
} JOBOBJECT_EXTENDED_LIMIT_INFORMATION,*PJOBOBJECT_EXTENDED_LIMIT_INFORMATION
IoInfo:保留成员,不应该以任何的方式访问它。
ProcessMemoryLimit/JobMomoryLimit:分别限制作业中的任何一个进程或者作业中全部进程所使用的已调拨的存储空间。
PeakProcessMemoryLimitUsed/PeakJobMemoryLimitUsed:只读,查看已调拨给作业的任何一个进程或者作业全部进程所需要的存储空间的峰值。
JOBOBJECT_BASIC_UI_RESTRICTIONS
这个结构体只有一个成员:DWORD UIRestrictionsClass;它的标志位如下表
JOBOBJECT_SECURITY_LIMIT_INFORMATION
typedef struct _JOBOBJECT_SECURITY_LIMIT_INFORMATION{
DWORD SecurityLimitFlags;
HANDLE JobToken;
PTOKEN_GROUPS SidsToDisable;
PTOKEN_PRIVILEGES PrivilegesToDelete;
PTOKEN_GROUPS RestrictedSids;
}JOBOBJECT_SECURITY_LIMIT_INFORMATION,*PJOBOBJECT_SECURITY_LIMIT_INFORMATION;
3.将进程放入作业中
BOOL AssignProcessToJobObject(HANDLE hJob,HANDLE hProcess);
函数只允许将尚未分配给任何作业的一个进程分配给一个作业,一旦进程已经属于作业的一部分就不能再移动到另一个作业中,并且作业中的一个进程生成了一个子进程的时候,新的子进程会自动成为父进程所属作业中的一部分。
4.终止作业中的所有进程。
BOOL TerminateJobObject(HANDLE hJob,UINT uExitCode);
5.一个作业内核对象的例子:
BOOL IsInJob=FALSE;
IsProcessInJob(GetCurrentProcess(),NULL,&IsInJob);
if(IsInJob)
{
return 0; //已经在另一个作业中的话,就不能添加到新的作业了。
}
HANDLE hJob=CreateJobObject(NULL,TEXT("MyNewJob")); //创建一个新的作业//现在对作业添加限制 使用 SetInformationJobObject函数参数中的 JOBOBJECT_BASIC_LIMIT_INFORMATION 类型的结构体 JOBOBJECT_BASIC_LIMIT_INFORMATION JBLI ={0}; //先全部初始化为空 因为只使用结构体中的部分成员限制部分属性
JBLI.LimitFlags= JOB_OBJECT_LIMIT_PRIORITY_CLASS|JOB_OBJECT_LIMIT_JOB_TIME; //这个标志说明要设置作业进程中的等级和作业运作时间
//所以设置结构体中成员的两个成员
JBLI.PriorityClass = IDLE_PRIORITY_CLASS; //设置作业中进程都是较低等级的,其中线程只有等系统空闲的时候才会执行
JBLI.PerJobUserTimeLimit.QuadPart=10000; //CPU调度不超过1秒 时间单位为100000纳秒 所以1秒//设置作业的属性SetInformationJobOBject(hJob,JobObjectBasicLimitInformation,&JBLI,sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION));
//创建一个新的CMD进程并添加到作业中
STARTUPINFO si = {sizeof(STARTUPINFO)}; //初始化并设置大小成员的值
PROCESS_INFORMATION pi;
TCHAR CmdFullPath[MAX_PATH]={0};
GetSystemDirectory(CmdFullPath,MAX_PATH); //获得系统目录_tcscat(CmdFullPath,TEXT("\\cmd.exe"));//要先将新进程的主线程挂起来,因为要先将新进程添加到作业中新进程有了作业的限制才能运行 CreateProcess(CmdFullPath,NULL,NULL,NULL,FALSE,CREATE_SUSPENDED|CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);
//添加到作业中
AssignProcessToJobObject(hJob,pi.hProcess); //之所以要传入pi是因为在这里需要新进程的进程句柄
//恢复主线程的运行
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);