Windows核心编程-作业

原文链接: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 }
原文地址:https://www.cnblogs.com/liangliangdetianxia/p/4033174.html