windows内核开发笔记五:JOB作业

windows内核开发笔记五:JOB作业

  •    作业
  •     例程说明
  •     创建作业
  •     作业限制和设置
  •     将进程添加到作业
  •     终止作业
  •    作业统计信息
  •     作业通知消息

(1) 作业

       作业对象允许一组进程被当做一个单元进行管理。作业对象是可命名的、安全的、共享的对象,它能够控制它包含的所有进程的属性。执行在作业上的操作会影响作业包含的所有进程。

     作业可视为进程的容器,可以对其中的所有进程加上限制条件。

     使用CrateJobObject函数,创建一个作业

     使用SetInformationJobObject函数,为作业添加限制条件

    使用AssignProcessToJobObject函数,将进程添加到作业

    使用IsProcessInJob函数,判断一个进程是否属于一个作业。

(2) 例程说明

 

 #include <windows.h>
 #include <tchar.h>
   
 int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR lpCmdLine,int nShowCmd)
{
       //创建一个作业内核对象
      HANDLE hJob = CreateJobObject(NULL,NULL); //
      
      //为作业添加一些基本限制
   
      //基本限制结构体
      JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = {0};
   
      //作业的优先级
      jobli.PriorityClass = IDLE_PRIORITY_CLASS; //
    
      //作业的CPU时间限制
      jobli.PerJobUserTimeLimit.QuadPart = 10000000; //1秒,单位是100纳秒
    
      //指明限制条件
      jobli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS|JOB_OBJECT_LIMIT_JOB_TIME;
   
      //设定作业限制
      SetInformationJobObject(hJob,JobObjectBasicLimitInformation,&jobli,sizeof(jobli));
   
      
      //为作业添加一些基本UI限制
   
      //基本UI限制结构体
      JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir;
   
      //初始无限制
      jobuir.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; //
   
      //增加限制:作业(进程)不能注销操作系统
      jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
   
      //增加限制:作业(进程)不能访问 系统的用户对象(如其他窗口)
      jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
   
      //设定作业限制
      SetInformationJobObject(hJob,JobObjectBasicUIRestrictions,&jobuir,sizeof(jobuir));
    
      
      //创建进程,并添加到作业中。进程初始化时必须是挂起状态,保证在添加到作业前不会执行任何代码
    
      //创建进程
      STARTUPINFO si={sizeof(si)};
      PROCESS_INFORMATION pi;
      CreateProcess(_T("C:\\Windows\\System32\\cmd.exe"),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi); //CREATE_SUSPENDED
   
      //将进程添加到作业
      AssignProcessToJobObject(hJob,pi.hProcess);
   
      //唤醒进程(的主线程)
      ResumeThread(pi.hThread);
       
      //关闭句柄
      CloseHandle(pi.hThread);
    
      
      //等待进程结束或作业CPU时间耗完
      HANDLE h[2];
      h[0] = pi.hProcess;
      h[1] = hJob;
   
      DWORD ret = WaitForMultipleObjects(2,h,FALSE,INFINITE);
      switch(ret-WAIT_OBJECT_0){
         case 0:
             //进程结束
             MessageBox(NULL,_T("进程结束"),_T("提示"),MB_OK);
             break;
         case 1:
             //作业分配的CPU时间耗完
             MessageBox(NULL,_T("时间耗尽"),_T("提示"),MB_OK);
             break;
     }
   
     //关闭句柄
     CloseHandle(pi.hProcess);
     CloseHandle(hJob);
   
     return 0;
}

(3) 创建作业

HANDLE WINAPI CreateJobObject( //创建作业内核对象

    __in_opt  LPSECURITY_ATTRIBUTES lpJobAttributes, //安全结构体

    __in_opt  LPCTSTR lpName   //名称,可以为NULl

    );

(4)作业限制和设置

作业限制类型有:基本限制、扩展限制、UI限制、安全性限制

使用SetInformationJobObject可以为作业指定限制。

BOOL WINAPI SetInformationJobObject(                            //设置作业限制 __in HANDLE hJob, //要添加限制的作业
    __in JOBOBJECTINFOCLASS JobObjectInfoClass,       //限制的类型 __in LPVOID lpJobObjectInfo, //限制的值
    __in DWORD cbJobObjectInfoLength                             //限制的值的长度
);

限制类型

说明

第二个参数的值

第三个参数的结构

基本限制

CPU分配限制

JobObjectBasicLimitInformation

JOBOBJECT_BASIC_LIMIT_INFORMATION

扩展限制

基本限制+内存分配限制

JobObjectExtendedLimitInformation

JOBOBJECT_EXTENDED_LIMIT_INFORMATION

基本UI限制

防止作业中进程改变UI

JobObjectBasicUIRestictions

JOBOBJECT_BASIC_UI_RESTRICTIONS

安全性限制

防止作业中进程访问保密资源

JobObjectSecurityLimitInformation

JOBOBJECT_SECURITY_LIMIT_INFORMATION

[1] 基本限制

 //基本限制:CPU限制
 typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION {
     LARGE_INTEGER PerProcessUserTimeLimit; //如果LimitFlags含有JOB_OBJECT_LIMIT_PROCESS_TIME,则此参数表示分配给每个进程的用户模式执行时间,单位100ns.超时进程会被终止
     LARGE_INTEGER PerJobUserTimeLimit;     //如果LimitFlags含有JOB_OBJECT_LIMIT_JOB_TIME,则此参数表示分配给作业的用户模式执行时间,超时作业会被终止
     DWORD         LimitFlags;              //指明哪些限制对作业有效
     SIZE_T        MinimumWorkingSetSize;   //如果LimitFlags含有JOB_OBJECT_LIMIT_WORKINGSET,则此参数表示作业中每个进程的最小工作集大小
     SIZE_T        MaximumWorkingSetSize;   //同上,最大工作集大小
     DWORD         ActiveProcessLimit;      //如果LimitFlags含有JOB_OBJECT_LIMIT_ACTIVE_PROCESS,则此参数表示作业中可以同时运行的最大进程数量
     ULONG_PTR     Affinity;                //如果LimitFlags含有JOB_OBJECT_LIMIT_AFFINITY,则此参数表示能够运行的进程的CPU子集
     DWORD         PriorityClass;           //如果LimitFlags含有JOB_OBJECT_LIMIT_PRIORITY_CLASS,则此参数表示作业中所有进程的优先级
     DWORD         SchedulingClass;         //如果LimitFlags含有JOB_OBJECT_LIMIT_SCHEDULING_CLASS,则此参数表示相同优先级的作业的调度优先级(0-9,默认5),值越大,CPU时间越长
 } JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION;

[2] 扩展限制

//扩展限制:基本限制+内存限制
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
     JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; //基本限制
     IO_COUNTERS                       IoInfo;                //保留不用。IO计数器
     SIZE_T                            ProcessMemoryLimit;    //每个进程能使用的内存量(“基本限制”参数的LimitFlags需含有JOB_OBJECT_LIMIT_PROCESS_MEMORY)
     SIZE_T                            JobMemoryLimit;        //作业(所有进程)能使用的内存量(“基本限制”参数的LimitFlags需含有JOB_OBJECT_LIMIT_JOB_MEMORY )
     SIZE_T                            PeakProcessMemoryUsed; //只读。单个进程需要使用的内存最大值
     SIZE_T                            PeakJobMemoryUsed;     //只读。作业需要使用的内存最大值
} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;

[3] 基本UI限制

//基本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

防止进程通过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) 将进程添加到作业 

要添加到作业的进程在创建时,需使用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) 终止作业 

BOOL WINAPI TerminateJobObject(
     __in  HANDLE hJob,    //作业
    __in  UINT uExitCode  //退出码。作业中所有进程的退出码自动设为uExitCode
    );

(7) 查询作业的统计信息

(8) 作业通知消息

创建一个IO完成端口(IO Completion Port)内核对象,然后将作业对象或多个作业对象与完成端口关联起来(使用SetInformationJobObject函数),然后让一个或多个线程在完成端口上等待作业通知的到来。 

  #include <windows.h>
  #include <process.h>  //_beginthreadex
  #include <tchar.h>
     
  #define CMPKEY_JOBOBJECT 1
  #define CMPKEY_TERMINATE 2
    
  typedef unsigned (__stdcall *PTHREAD_START) (void *);
     
  //IO完成端口监听线程回调函数
  DWORD WINAPI JobNotify(LPVOID lpParam)
  {
      HANDLE hIOCP = (HANDLE)lpParam;
    
      while (TRUE)
      {
          DWORD dwBytesTransferred;
          ULONG_PTR CompKey;
          LPOVERLAPPED po;
    
          //从IO完成端口中获取一个消息
          GetQueuedCompletionStatus(hIOCP,&dwBytesTransferred,&CompKey,&po,INFINITE);
    
          //退出消息
          if (CompKey == CMPKEY_TERMINATE)
          {
              MessageBox(NULL,_T("监听线程退出"),_T("提示"),MB_OK);
              break;
          }
    
          //来自作业对象hJob的消息
          if(CompKey == CMPKEY_JOBOBJECT)
          {
              MessageBox(NULL,_T("收到来自作业的消息"),_T("提示"),MB_OK);
    
              switch(dwBytesTransferred){
                 case JOB_OBJECT_MSG_END_OF_JOB_TIME:
                    MessageBox(NULL,_T("作业限制时间耗尽"),_T("提示"),MB_OK);
                    break;
                 case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
                    {
                        TCHAR szProcessName[MAX_PATH];
                        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(DWORD)po);
    
                        if(hProcess == NULL){
                            _stprintf(szProcessName,_T("%s"),_T("未知进程名"));
                        }
                        else{
                            DWORD dwSize = (DWORD)MAX_PATH;
                            QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize);
                            CloseHandle(hProcess);
                        }
    
                       TCHAR info[MAX_PATH];
                        _stprintf(info,_T("进程%s(ID=%d)限制时间耗尽 "),szProcessName,po);
    
                        MessageBox(NULL,info,_T("提示"),MB_OK);
                    }
                    break;
                 case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
                    MessageBox(NULL,_T("运行的进程超过限制"),_T("提示"),MB_OK);
                    break;
                case JOB_OBJECT_MSG_NEW_PROCESS:
                    MessageBox(NULL,_T("作业中产生新进程"),_T("提示"),MB_OK);
                    break;
                case JOB_OBJECT_MSG_EXIT_PROCESS:  {
                    TCHAR szProcessName[MAX_PATH];
                    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(DWORD)po);
   
                    if(hProcess == NULL){
                          _stprintf(szProcessName,_T("%s"),_T("未知进程名"));
                    }
                    else{
                           DWORD dwSize = (DWORD)MAX_PATH;
                           QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize);
                           CloseHandle(hProcess);
                    }   
                    TCHAR info[MAX_PATH];
                     _stprintf(info,_T("进程%s(ID=%d)终止 "),szProcessName,po);
    
                     MessageBox(NULL,info,_T("提示"),MB_OK);
                 }
                 break;
             }
         }
    }
     return 0;
}
 
int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR lpCmdLine,int nShowCmd)
{
  
     //创建一个作业内核对象
     HANDLE hJob = CreateJobObject(NULL,NULL); //
  
     //创建一个IO完成端口
     HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
  
     //创建一个线程监听IO完成端口通知消息
     HANDLE hThreadIOCP = (HANDLE)_beginthreadex(NULL,0,(PTHREAD_START)JobNotify,(LPVOID)hIOCP,0,NULL);
   
     //将IO完成端口与作业关联
     JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobacp;
     jobacp.CompletionKey = (PVOID)CMPKEY_JOBOBJECT;  //任意一个全局唯一的值
     jobacp.CompletionPort = hIOCP;                   //IO完成端口句柄
     SetInformationJobObject(hJob,JobObjectAssociateCompletionPortInformation,&jobacp,sizeof(jobacp)); //关联
      
     
     //创建进程,并添加到作业中。进程初始化时必须是挂起状态,保证在添加到作业前不会执行任何代码
   
     STARTUPINFO si={sizeof(si)};
     PROCESS_INFORMATION pi;
     CreateProcess(_T("C:\\Windows\\System32\\cmd.exe"),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi); //CREATE_SUSPENDED
     AssignProcessToJobObject(hJob,pi.hProcess);//将进程添加到作业
     MessageBox(NULL,_T("111"),_T("Tips"),MB_OK);
     ResumeThread(pi.hThread);//唤醒进程(的主线程)
     CloseHandle(pi.hThread); //关闭句柄
     CloseHandle(pi.hProcess);
     MessageBox(NULL,_T("MESSAGE"),_T("Tips"),MB_OK);
   
     //发送一条消息给IO完成端口,结束IO完成端口线程
     PostQueuedCompletionStatus(hIOCP,0,CMPKEY_TERMINATE,NULL);
   
     //等待IO完成端口线程终止
     WaitForSingleObject(hThreadIOCP,INFINITE);
   
     //关闭句柄
     CloseHandle(hIOCP);
     CloseHandle(hThreadIOCP);
     CloseHandle(hJob);
     return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jyl_sh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值