• 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 }
  • 相关阅读:
    for循环
    3.9 作业
    while循环
    深浅拷贝
    条件与判断
    可变与不可变
    与用户交互与运算符
    垃圾回收机制
    【AC自动机】文本生成器
    【AC自动机】最短母串
  • 原文地址:https://www.cnblogs.com/liangliangdetianxia/p/4033174.html
Copyright © 2020-2023  润新知