原文链接:http://zhujiangtao.com/?p=983
作业
- 作业
- 一个简单例程
- CreateJobObject 创建作业
- 作业限制和 SetInformationJobObject
- AssignProcessToJobObject 将进程添加到作业
- 终止作业
- QueryInformationJobObject 查询作业的统计信息
- 作业的通知消息
(1) 作业
[MSDN] 作业对象允许一组进程被当做一个单元进行管理。作业对象是可命名的、安全的、共享的对象,它能够控制它包含的所有进程的属性。执行在作业上的操作会影响作业包含的所有进程。
作业可视为进程的容器,可以对其中的所有进程加上限制条件。
使用CrateJobObject函数,创建一个作业
使用SetInformationJobObject函数,为作业添加限制条件
使用AssignProcessToJobObject函数,将进程添加到作业
使用IsProcessInJob函数,判断一个进程是否属于一个作业。
(2) 一个简单例程
1 #include <windows.h> 2 #include <tchar.h> 3 4 int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR lpCmdLine,int nShowCmd){ 5 6 //创建一个作业内核对象 7 HANDLE hJob = CreateJobObject(NULL,NULL); // 8 9 //////////////////////////////////////////////////////////// 10 //为作业添加一些基本限制 11 12 //基本限制结构体 13 JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = {0}; 14 15 //作业的优先级 16 jobli.PriorityClass = IDLE_PRIORITY_CLASS; // 17 18 //作业的CPU时间限制 19 jobli.PerJobUserTimeLimit.QuadPart = 10000000; //1秒,单位是100纳秒 20 21 //指明限制条件 22 jobli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS|JOB_OBJECT_LIMIT_JOB_TIME; 23 24 //设定作业限制 25 SetInformationJobObject(hJob,JobObjectBasicLimitInformation,&jobli,sizeof(jobli)); 26 27 //////////////////////////////////////////////////////////// 28 //为作业添加一些基本UI限制 29 30 //基本UI限制结构体 31 JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir; 32 33 //初始无限制 34 jobuir.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; // 35 36 //增加限制:作业(进程)不能注销操作系统 37 jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; 38 39 //增加限制:作业(进程)不能访问 系统的用户对象(如其他窗口) 40 jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; 41 42 //设定作业限制 43 SetInformationJobObject(hJob,JobObjectBasicUIRestrictions,&jobuir,sizeof(jobuir)); 44 45 //////////////////////////////////////////////////////////// 46 //创建进程,并添加到作业中。进程初始化时必须是挂起状态,保证在添加到作业前不会执行任何代码 47 48 //创建进程 49 STARTUPINFO si={sizeof(si)}; 50 PROCESS_INFORMATION pi; 51 CreateProcess(_T("C:\\Windows\\System32\\cmd.exe"),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi); //CREATE_SUSPENDED 52 53 //将进程添加到作业 54 AssignProcessToJobObject(hJob,pi.hProcess); 55 56 //唤醒进程(的主线程) 57 ResumeThread(pi.hThread); 58 59 //关闭句柄 60 CloseHandle(pi.hThread); 61 62 //////////////////////////////////////////////////////////// 63 //等待进程结束或作业CPU时间耗完 64 HANDLE h[2]; 65 h[0] = pi.hProcess; 66 h[1] = hJob; 67 68 DWORD ret = WaitForMultipleObjects(2,h,FALSE,INFINITE); 69 switch(ret-WAIT_OBJECT_0){ 70 case 0: 71 //进程结束 72 MessageBox(NULL,_T("进程结束"),_T("提示"),MB_OK); 73 break; 74 case 1: 75 //作业分配的CPU时间耗完 76 MessageBox(NULL,_T("时间耗尽"),_T("提示"),MB_OK); 77 break; 78 } 79 80 //关闭句柄 81 CloseHandle(pi.hProcess); 82 CloseHandle(hJob); 83 84 return 0; 85 }
(3) CreateJobObject 创建作业
HANDLE
WINAPI CreateJobObject(
//创建作业内核对象
__in_opt LPSECURITY_ATTRIBUTES lpJobAttributes,
//安全结构体
__in_opt
LPCTSTR
lpName
//名称,可以为NULl
);
(4)作业限制 和 SetInformationJobObject
作业限制类型有:基本限制、扩展限制、UI限制、安全性限制
使用SetInformationJobObject可以为作业指定限制。
1 BOOL WINAPI SetInformationJobObject( //设置作业限制 2 __in HANDLE hJob, //要添加限制的作业 3 __in JOBOBJECTINFOCLASS JobObjectInfoClass, //限制的类型 4 __in LPVOID lpJobObjectInfo, //限制的值 5 __in DWORD cbJobObjectInfoLength //限制的值的长度 6 );
限制类型 |
说明 |
第二个参数的值 |
第三个参数的结构 |
基本限制 |
CPU分配限制 |
JobObjectBasicLimitInformation |
JOBOBJECT_BASIC_LIMIT_INFORMATION |
扩展限制 |
基本限制+内存分配限制 |
JobObjectExtendedLimitInformation |
JOBOBJECT_EXTENDED_LIMIT_INFORMATION |
基本UI限制 |
防止作业中进程改变UI |
JobObjectBasicUIRestictions |
JOBOBJECT_BASIC_UI_RESTRICTIONS |
安全性限制 |
防止作业中进程访问保密资源 |
JobObjectSecurityLimitInformation |
JOBOBJECT_SECURITY_LIMIT_INFORMATION |
[1] 基本限制
1 //基本限制:CPU限制 2 typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION { 3 LARGE_INTEGER PerProcessUserTimeLimit; //如果LimitFlags含有JOB_OBJECT_LIMIT_PROCESS_TIME,则此参数表示分配给每个进程的用户模式执行时间,单位100ns.超时进程会被终止 4 LARGE_INTEGER PerJobUserTimeLimit; //如果LimitFlags含有JOB_OBJECT_LIMIT_JOB_TIME,则此参数表示分配给作业的用户模式执行时间,超时作业会被终止 5 DWORD LimitFlags; //指明哪些限制对作业有效 6 SIZE_T MinimumWorkingSetSize; //如果LimitFlags含有JOB_OBJECT_LIMIT_WORKINGSET,则此参数表示作业中每个进程的最小工作集大小 7 SIZE_T MaximumWorkingSetSize; //同上,最大工作集大小 8 DWORD ActiveProcessLimit; //如果LimitFlags含有JOB_OBJECT_LIMIT_ACTIVE_PROCESS,则此参数表示作业中可以同时运行的最大进程数量 9 ULONG_PTR Affinity; //如果LimitFlags含有JOB_OBJECT_LIMIT_AFFINITY,则此参数表示能够运行的进程的CPU子集 10 DWORD PriorityClass; //如果LimitFlags含有JOB_OBJECT_LIMIT_PRIORITY_CLASS,则此参数表示作业中所有进程的优先级 11 DWORD SchedulingClass; //如果LimitFlags含有JOB_OBJECT_LIMIT_SCHEDULING_CLASS,则此参数表示相同优先级的作业的调度优先级(0-9,默认5),值越大,CPU时间越长 12 } JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION;
[2] 扩展限制
1 //扩展限制:基本限制+内存限制 2 typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION { 3 JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; //基本限制 4 IO_COUNTERS IoInfo; //保留不用。IO计数器 5 SIZE_T ProcessMemoryLimit; //每个进程能使用的内存量(“基本限制”参数的LimitFlags需含有JOB_OBJECT_LIMIT_PROCESS_MEMORY) 6 SIZE_T JobMemoryLimit; //作业(所有进程)能使用的内存量(“基本限制”参数的LimitFlags需含有JOB_OBJECT_LIMIT_JOB_MEMORY ) 7 SIZE_T PeakProcessMemoryUsed; //只读。单个进程需要使用的内存最大值 8 SIZE_T PeakJobMemoryUsed; //只读。作业需要使用的内存最大值 9 } JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
[3] 基本UI限制
1 //基本UI限制 2 typedef struct _JOBOBJECT_BASIC_UI_RESTRICTIONS { 3 DWORD UIRestrictionsClass; //下表标志中一个或是组合 4 } 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 |
防止进程通过SystemParametersInfor函数来改变系统参数 |
JOB_OBJECT_UILIMIT_DISPLAYSETTINGS |
防止进程通过ChangeDisplaySettings函数来改变显示设置 |
JOB_OBJECT_UILIMIT_GLOBALATOMS |
防止进程访问全局的基本结构表,为作业分配自己的基本结构表,作业中进程只能访问该表。 |
JOB_OBJECT_UILIMIT_DESKTOP |
防止进程使用CreateDesktop或SwitchDesktop函数创建或转换桌面 |
JOB_OBJECT_UILIMIT_HANDLES |
防止进程使用作业外部的进程创建的用户对象的句柄(如HWND) |
[4] 安全性限制
Windows XP(不包括XP)之后的系统不再支持该限制,需要为每个进程单独指定安全设置。
(5)AssignProcessToJobObject 将进程添加到作业
要添加到作业的进程在创建时,需使用CREATE_SUSPEND标志,防止加入作业前进程执行任何代码。
1 BOOL WINAPI AssignProcessToJobObject( __in HANDLE hJob, //作业句柄 2 __in HANDLE hProcess //进程句柄 3 );
一个进程加入到一个作业后,不能再转到另一个作业。
作业中的进程生成的新进程会自动成为作业的一部分。可以通过下面两种方法改变这种特性:
[1] 打开JOBOBJECT_BASIC_LIMIT_INFROMATION 的LimitFlags成员的JOB_OBJECT_BREAKAWAY_OK标志,告诉系统,新生成的进程可以在作业外部运行。同时使用CREATE_BREAKAWAY_FROM_JOB 标志调用CreateProcess创建新进程
[2] 打开JOBOBJECT_BASIC_LIMIT_INFROMATION 的LimitFlags成员的JOB_OBJECT_SILENT_BREAKAWAY_OK标志,告诉系统,新生成的进程可以在作业外部运行。
(6) 终止作业
1 BOOL WINAPI TerminateJobObject( 2 __in HANDLE hJob, //作业 3 __in UINT uExitCode //退出码。作业中所有进程的退出码自动设为uExitCode 4 );
(7) QueryInformationJobObject 查询作业的统计信息
(8) 作业的通知消息
创建一个IO完成端口(IO Completion Port)内核对象,然后将作业对象或多个作业对象与完成端口关联起来(使用SetInformationJobObject函数),然后让一个或多个线程在完成端口上等待作业通知的到来。
1 #include <windows.h> 2 #include <process.h> //_beginthreadex 3 #include <tchar.h> 4 5 #define CMPKEY_JOBOBJECT 1 6 #define CMPKEY_TERMINATE 2 7 8 typedef unsigned (__stdcall *PTHREAD_START) (void *); 9 10 //IO完成端口监听线程回调函数 11 DWORD WINAPI JobNotify(LPVOID lpParam) 12 { 13 HANDLE hIOCP = (HANDLE)lpParam; 14 15 while (TRUE) 16 { 17 DWORD dwBytesTransferred; 18 ULONG_PTR CompKey; 19 LPOVERLAPPED po; 20 21 //从IO完成端口中获取一个消息 22 GetQueuedCompletionStatus(hIOCP,&dwBytesTransferred,&CompKey,&po,INFINITE); 23 24 //退出消息 25 if (CompKey == CMPKEY_TERMINATE) 26 { 27 MessageBox(NULL,_T("监听线程退出"),_T("提示"),MB_OK); 28 break; 29 } 30 31 //来自作业对象hJob的消息 32 if(CompKey == CMPKEY_JOBOBJECT) 33 { 34 MessageBox(NULL,_T("收到来自作业的消息"),_T("提示"),MB_OK); 35 36 switch(dwBytesTransferred){ 37 case JOB_OBJECT_MSG_END_OF_JOB_TIME: 38 MessageBox(NULL,_T("作业限制时间耗尽"),_T("提示"),MB_OK); 39 break; 40 case JOB_OBJECT_MSG_END_OF_PROCESS_TIME: 41 { 42 TCHAR szProcessName[MAX_PATH]; 43 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(DWORD)po); 44 45 if(hProcess == NULL){ 46 _stprintf(szProcessName,_T("%s"),_T("未知进程名")); 47 } 48 else{ 49 DWORD dwSize = (DWORD)MAX_PATH; 50 QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize); 51 CloseHandle(hProcess); 52 } 53 54 TCHAR info[MAX_PATH]; 55 _stprintf(info,_T("进程%s(ID=%d)限制时间耗尽 "),szProcessName,po); 56 57 MessageBox(NULL,info,_T("提示"),MB_OK); 58 } 59 break; 60 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: 61 MessageBox(NULL,_T("运行的进程超过限制"),_T("提示"),MB_OK); 62 break; 63 case JOB_OBJECT_MSG_NEW_PROCESS: 64 MessageBox(NULL,_T("作业中产生新进程"),_T("提示"),MB_OK); 65 break; 66 case JOB_OBJECT_MSG_EXIT_PROCESS: { 67 TCHAR szProcessName[MAX_PATH]; 68 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(DWORD)po); 69 70 if(hProcess == NULL){ 71 _stprintf(szProcessName,_T("%s"),_T("未知进程名")); 72 } 73 else{ 74 DWORD dwSize = (DWORD)MAX_PATH; 75 QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize); 76 CloseHandle(hProcess); 77 } 78 79 TCHAR info[MAX_PATH]; 80 _stprintf(info,_T("进程%s(ID=%d)终止 "),szProcessName,po); 81 82 MessageBox(NULL,info,_T("提示"),MB_OK); 83 } 84 break; 85 } 86 } 87 } 88 return 0; 89 } 90 91 int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR lpCmdLine,int nShowCmd){ 92 93 //创建一个作业内核对象 94 HANDLE hJob = CreateJobObject(NULL,NULL); // 95 96 //创建一个IO完成端口 97 HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0); 98 99 //创建一个线程监听IO完成端口通知消息 100 HANDLE hThreadIOCP = (HANDLE)_beginthreadex(NULL,0,(PTHREAD_START)JobNotify,(LPVOID)hIOCP,0,NULL); 101 102 //将IO完成端口与作业关联 103 JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobacp; 104 jobacp.CompletionKey = (PVOID)CMPKEY_JOBOBJECT; //任意一个全局唯一的值 105 jobacp.CompletionPort = hIOCP; //IO完成端口句柄 106 SetInformationJobObject(hJob,JobObjectAssociateCompletionPortInformation,&jobacp,sizeof(jobacp)); //关联 107 108 //////////////////////////////////////////////////////////// 109 //创建进程,并添加到作业中。进程初始化时必须是挂起状态,保证在添加到作业前不会执行任何代码 110 111 STARTUPINFO si={sizeof(si)}; 112 PROCESS_INFORMATION pi; 113 CreateProcess(_T("C:\\Windows\\System32\\cmd.exe"),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi); //CREATE_SUSPENDED 114 AssignProcessToJobObject(hJob,pi.hProcess);//将进程添加到作业 115 MessageBox(NULL,_T("111"),_T("Tips"),MB_OK); 116 ResumeThread(pi.hThread);//唤醒进程(的主线程) 117 CloseHandle(pi.hThread); //关闭句柄 118 CloseHandle(pi.hProcess); 119 MessageBox(NULL,_T("MESSAGE"),_T("Tips"),MB_OK); 120 121 //发送一条消息给IO完成端口,结束IO完成端口线程 122 PostQueuedCompletionStatus(hIOCP,0,CMPKEY_TERMINATE,NULL); 123 124 //等待IO完成端口线程终止 125 WaitForSingleObject(hThreadIOCP,INFINITE); 126 127 //关闭句柄 128 CloseHandle(hIOCP); 129 CloseHandle(hThreadIOCP); 130 CloseHandle(hJob); 131 return 0; 132 }