windows核心编程(五)作业内核对象

来源:互联网 发布:易分销 源码 编辑:程序博客网 时间:2024/05/21 17:38
(1)作业定义(作业内核对象)
可以想象为一个进程组的容器,可以集中管理一组进程。包括进程使用的最大CPU时间、最大工作集和最小工作集、禁止应用程序关闭、计算机以及安全限制。若作业内只有一个进程时,上述的限制同样适用,可以对进程施加平时不能施加的限制。

(2)先写例子,再解释例子:

void StartRestrictedProcess(){//检查进程是否已经在一个作业内,若已经在作业内,则不能再添加到其他的作业内。BOOL bInJod = FALSE;IsProcessInJob(GetCurrentProcess(),NULL,&bInJod);if (bInJod){MessageBox(NULL,_T("此进程已经在一个作业内!"),_T("提示"),MB_OK | MB_ICONINFORMATION);return;}//创建一个作业内核对象HANDLE hJob = CreateJobObject(NULL,_T("JOB_NAME_TEST"));//给作业内的进程添加一些限制//添加基本限制JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = {0};jobli.PriorityClass = IDLE_PRIORITY_CLASS;jobli.PerJobUserTimeLimit.QuadPart = 1000;//jobli.PerProcessUserTimeLimit.QuadPart = 1000;jobli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS | JOB_OBJECT_LIMIT_JOB_TIME;// | JOB_OBJECT_LIMIT_PROCESS_TIME;SetInformationJobObject(hJob,JobObjectBasicLimitInformation,&jobli,sizeof(jobli));//添加UI限制JOBOBJECT_BASIC_UI_RESTRICTIONS jobui;jobui.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;//限制进程使用ExitWindows函数关闭进程jobui.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;//限制进程接受系统中的用户句柄SetInformationJobObject(hJob,JobObjectBasicUIRestrictions,&jobui,sizeof(jobui));//创建一个进程将其放在做业内,创建完成后必须先挂起进程,等添加到作业内才是其开始执行。STARTUPINFO si = {sizeof(si)};PROCESS_INFORMATION pi;TCHAR szCmd[8];ZeroMemory(szCmd,sizeof(szCmd));_tcscpy_s(szCmd,_countof(szCmd),_T("CMD"));BOOL bResult = CreateProcess(NULL,szCmd,NULL,NULL,FALSE,CREATE_SUSPENDED | CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);//添加进程的作业AssignProcessToJobObject(hJob,pi.hProcess);ResumeThread(pi.hThread);CloseHandle(pi.hThread);//等待作业内的进程结束 或者 显示作业分配的CPU时间HANDLE h[2];h[0] = pi.hProcess;h[1] = hJob;FILETIME CreateionTime;FILETIME ExitTime;FILETIME KernelTime;FILETIME UserTime;TCHAR szInfo[MAX_PATH];DWORD dwRet = WaitForMultipleObjects(2,h,FALSE,INFINITE);switch (dwRet - WAIT_OBJECT_0){case 0:{//进程结束GetProcessTimes(pi.hProcess,&CreateionTime,&ExitTime,&KernelTime,&UserTime);ZeroMemory(szInfo,sizeof(szInfo));StringCchPrintf(szInfo,_countof(szInfo),_T("KernelTime=%u | UserTime = %u"),KernelTime.dwLowDateTime/10000,UserTime.dwLowDateTime/10000);MessageBox(NULL,szInfo,_T("hProcess限制的进程时间"),MB_ICONINFORMATION | MB_OK);}break;case 1:{//分配的CPU时间GetProcessTimes(hJob,&CreateionTime,&ExitTime,&KernelTime,&UserTime);ZeroMemory(szInfo,sizeof(szInfo));StringCchPrintf(szInfo,_countof(szInfo),_T("KernelTime=%u | UserTime = %u"),KernelTime.dwLowDateTime/10000,UserTime.dwLowDateTime/10000);MessageBox(NULL,szInfo,_T("hJob限制的进程时间"),MB_ICONINFORMATION | MB_OK);}break;default:break;}GetProcessTimes(pi.hProcess,&CreateionTime,&ExitTime,&KernelTime,&UserTime);ZeroMemory(szInfo,sizeof(szInfo));StringCchPrintf(szInfo,_countof(szInfo),_T("KernelTime=%u | UserTime = %u"),KernelTime.dwLowDateTime/10000,UserTime.dwLowDateTime/10000);MessageBox(NULL,szInfo,_T("hProcess限制的进程时间"),MB_ICONINFORMATION | MB_OK);CloseHandle(pi.hProcess);CloseHandle(hJob);}
代码解释:
(2.。1)使用如下函数检查,当前进程是否已经在一个作业内:

BOOL WINAPI IsProcessInJob(  __in          HANDLE ProcessHandle,  __in          HANDLE JobHandle,  __out         PBOOL Result);
(2.。2)使用如下函数可以创建一个作业,返回一个作业内核对象:

HANDLE WINAPI CreateJobObject(  __in          LPSECURITY_ATTRIBUTES lpJobAttributes,  __in          LPCTSTR lpName);
可以创建一个命令的作业内核对象,然后其他进程可以使用OpenJobObject函数发开一个作业。
(2.。3)不再访问作业对象的话,同样需要关闭作业内核对象。(注:关闭作业的内核对象的句柄会导致所有的进程都不可访问此作业,即使作业仍然存在。

(2.。4)对作业中的进程施加限制
向作业应用以下几种类型的限制:
基本限额和扩展基本限额:用于防止作业中的进程独占系统资源
基本的UI限制:用于防止作业内的进程更改用户界面
安装限额:用于防止作业内的进程访问安全资源(文件,注册表等等)
调用如下的函数施加限制:

BOOL WINAPI SetInformationJobObject(  __in          HANDLE hJob,  __in          JOBOBJECTINFOCLASS JobObjectInfoClass,  __in          LPVOID lpJobObjectInfo,  __in          DWORD cbJobObjectInfoLength);
参数JOBOBJECTINFOCLASS JobObjectInfoClass是枚举类型的值,指定要施加的限制类型,LPVOID lpJobObjectInfo对应的JobObjectInfoClass的值。可以设置为:

    JobObjectBasicLimitInformation, //JOBOBJECT_BASIC_LIMIT_INFORMATION    JobObjectBasicUIRestrictions, //JOBOBJECT_BASIC_UI_RESTRICTIONS    JobObjectSecurityLimitInformation,//JOBOBJECT_SECURITY_LIMIT_INFORMATION      JobObjectExtendedLimitInformation, //JOBOBJECT_EXTENDED_LIMIT_INFORMATION
JOBOBJECT_BASIC_LIMIT_INFORMATION结构体,如下所示:
typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION {LARGE_INTEGER PerProcessUserTimeLimit;LARGE_INTEGER PerJobUserTimeLimit; DWORD LimitFlags; SIZE_T MinimumWorkingSetSize;  SIZE_T MaximumWorkingSetSize; DWORD ActiveProcessLimit; ULONG_PTR Affinity; DWORD PriorityClass;  DWORD SchedulingClass;} JOBOBJECT_BASIC_LIMIT_INFORMATION,  *PJOBOBJECT_BASIC_LIMIT_INFORMATION;
PerProcessUserTimeLimit限制分配给每个进程的最大用户模式时间(时间间隔为100ns)。对于占用时间超过其分配时间的任何进程,系统将自动终止它的运行。
PerJobUserTimeLimit:限制分配给作业对象的最大用户模式时间(时间间隔为100ns)默认情况下,在达到该时间的限额内,系统将自动终止所有进程的运行(可以在作业运行时定期的改变这个值)。
LimitFlags:指定将哪些限制应用于作业(标志位的与或操作)。
MinimumWorkingSetSize、MaximumWorkingSetSize:指定每个进程(并不是作业中的所有进程)的最小工作集和最大工作集。一旦进程的工作集抵达这个限额后,进程就开始进行换页操作。作业内的进程调用SetProcessWorkingSize函数时,只能将自己的工作集清零,其他操作将被忽略。
ActiveProcessLimit:指定作业内并发运行的进程的最大数。超过此限额的任何尝试都会导致新进程的终止,并报告一个“配额不足”的错误。使用基本扩展结构时,能够返回超过限制后终止的进程数目。
Affinity:指定能够运行进程的CPU子集。单独的进程可以进一步的对此进行限定。
PriorityClass:指定关联的所有进程的优先级类。进程内再调用SetPriorityClass和GetPriorityClass函数将受到影响。
SchedulingClass:为作业内的线程指定一个相对的时间量差。貌似是表示多个作业间的优先级。

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 PeakProcessMemoryUsed;    SIZE_T PeakJobMemoryUsed;} JOBOBJECT_EXTENDED_LIMIT_INFORMATION,  *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
此结构为基本限额的超集。
BasicLimitInformation:基本限额的结构体。
IoInfo:保留,不能以任何方式访问它。
ProcessMemoryLimit和JobMemoryLimit分别限制作业中的任何一个进程或全部进程所使用的已调拨的存储空间。为了设置这样的限制,需要在LimitFlags成员中分别指定JOB_OBJECT_LIMIT_PROCESS_MEMORY和JOB_OBJECT_LIMIT_JOB_MEMORY标志。
PeakProcessMemoryUsed和PeakJobMemoryUsed为只读的。表示已调拨给作业中的任何一个进程所需的存储空间的峰值和已调拨给作业中全部进程所需的存储空间的峰值。、


JOBOBJECT_BASIC_UI_RESTRICTIONS基本UI限制结构体,如下:

typedef struct _JOBOBJECT_BASIC_UI_RESTRICTIONS {  DWORD UIRestrictionsClass;} JOBOBJECT_BASIC_UI_RESTRICTIONS,  *PJOBOBJECT_BASIC_UI_RESTRICTIONS;
仅仅有一个成员变量,以下为所容纳的标志位集合。
JOB_OBJECT_UILIMIT_EXITWINDOWS:阻止进程通过ExitWindowsEx函数注销、关机、重启、或断开系统电源。
JOB_OBJECT_UILIMIT_READCLIPBOARD:阻止进程读取剪贴板中的内容
JOB_OBJECT_UILIMIT_WRITECLIPBOARD:阻止进程清除剪贴板中的内容
JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS:阻止进程通过SystemParametersInfo函数更改系统参数。
JOB_OBJECT_UILIMIT_DISPLAYSETTINGS:阻止进程通过ChangeDisplaySettings函数更改显示设置。
JOB_OBJECT_UILIMIT_GLOBALATOMS:为作业指定其专有的全局原于表,并限定作业中的进程只能访问此作业表。
JOB_OBJECT_UILIMIT_DESKTOP:阻止进程使用CreateDesktop或者SwithDesktop函数来创建或切换桌面
JOB_OBJECT_UILIMIT_HANDLES:阻止作业中的进程使用同一个作业外部的进程所创建的用户对象。
注:限制作业内的进程和作业外的进程进行消息通讯时,可以使用函数BOOL WINAPI UserHandleGrantAccess()来修改作业外的进程的用户句柄的访问限制。


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;
SecurityLimitFlags:指定是否不允许管理员访问,不允许无限制的令牌访问,强制使用特定的访问令牌,或禁止特定的安全标示符(security  idetifier,SID)或特权。
JobToken:作业中所有进程使用的访问令牌。
SidsToDisable:指定要禁止对哪些SID进行访问访问检查。
PrivilegesToDelete:指定要从访问令牌中删除哪些特权。
RestrictedSids:(MSDN的解释)A pointer to a TOKEN_GROUPS structure that specifies the deny-only SIDs that will be added to the access token, if SecurityLimitFlags is JOB_OBJECT_SECURITY_FILTER_TOKENS. 

(3)对作业施加限制的查询
使用如下函数对作业的限制的查询:

BOOL WINAPI QueryInformationJobObject(  __in          HANDLE hJob,  __in          JOBOBJECTINFOCLASS JobObjectInfoClass,  __out         LPVOID lpJobObjectInfo,  __in          DWORD cbJobObjectInfoLength,  __out         LPDWORD lpReturnLength);
(4)将进程放到作业内
使用如下函数:
BOOL WINAPI AssignProcessToJobObject(  __in          HANDLE hJob,  __in          HANDLE hProcess);

注:在创建子进程的时候,若希望将子进行再作业外运行,需要在CreateProcess的时候,使用标志:CREATE_BREAKAWAY_FROM_JOB,此时若作业没有在JOBOBJECT_BASIC_LIMIT_INFORMATION 的LimitFlags成员中设置JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK标志,则CreateProcess就会调用失败。
(5)终止作业内的所有进程
使用如下函数:

BOOL WINAPI TerminateJobObject(  __in          HANDLE hJob,  __in          UINT uExitCode);

(6)查询作业统计信息
首先需要调用函数QueryInformationJobObject函数,向第二个参数传递JobObjectBasicAccountingInformation和一个JOBOBJECT_BASIC_ACCOUNTING_INFORMATION的结构体的地址。结构体如下:
typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION {  LARGE_INTEGER TotalUserTime;   LARGE_INTEGER TotalKernelTime;  LARGE_INTEGER ThisPeriodTotalUserTime;  LARGE_INTEGER ThisPeriodTotalKernelTime;   DWORD TotalPageFaultCount;  DWORD TotalProcesses; DWORD ActiveProcesses;   DWORD TotalTerminatedProcesses;} JOBOBJECT_BASIC_ACCOUNTING_INFORMATION,  *PJOBOBJECT_BASIC_ACCOUNTING_INFORMATION;
TotalUserTime:指出作业中的进程已使用了多少用户模式的CPU时间
TotalKernelTime:指出作业中的进程已使用了多少内核模式的CPU时间
ThisPeriodTotalUserTime:与TotalUserTime一样,不同的是,如果调用SetInformationJobObject来更改基本限额信息,同时没有使用JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME限额标志,这个值被重置为0
ThisPeriodTotalKernelTime:同ThisPeriodTotalUserTime一样,这个是显示内核模式的CPU时间。
TotalPageFaultCount:指出作业中进程产生的页面错误的总数。
TotalProcesses:指出曾经属于作业一部分的所有进程的总数。
ActiveProcesses:指出作业的当前进程的总数
TotalTerminatedProcesses:指出因为超过预定CPU时间限额而被“杀死”的进程数

还有一个查询IO的结构体,需要的时候可以查看相应的函数。

(7)作业通知
后续补充。。。

原创粉丝点击