Windows Job简介

一直以来对这个东西总是搞不清楚,看了几遍Windows核心编程后,总算是有一点点感觉了,欢迎高手对我不正确的理解拍砖指正。

Job可以被理解为进程的容器,而它又不是单纯的容器,按照书本上所讲的,Job是一个沙盒,它可以为在它里面运行的进程增加一系列的限制。包括每一个进程或者整个Job能够使用的User mode CPU时间,每一个进程或者整个Job最多能使用的内存,Job内的进程能否访问到Job外的用户对象(例如窗口,画刷),能否退出Windows,能否访问剪切板等等。当限制设定之后,我们就可以创建一个进程,并将它放置到Job之中。

需要注意的是:

  • Job对象即使引用数到了0也不会立刻释放,它会等到Job内所有的进程都结束了再释放,但是,在这种情况下,Job的名称将会失效,不能再通过Job的名称和Job的句柄来向Job中增加新的进程。
  • Job可以设置当前可运行进程最大数量。当超过这个最大数量时,任何新进程都将被立刻终止(Terminate)。
  • Job可以在同一进程优先级下设定调度的微调值(SchedulingClass)。微调值高的Job中的线程比微调值低的获得更多的CPU时间。
  • Job可以限制对用户对象以及Windows界面的访问,例如不许退出窗口,不能访问剪切板等。这意味着Job内的进程无法获取到Job外进程的对象,比如HWND,但是Job外的进程可以获得Job内进程的对象。
  • 对Job设定了OBOBJECT_SECURITY_LIMIT_INFORMATION后,该设定不能被取消。
  • 新建的进程最好使用CREATE_SUSPEND Flag,这样新进程启动后在加入到Job之前它都无法运行,从而避免新进程可以逃出沙盒。在加入到Job之后,继续运行进程即可。

当一个进程被加入到Job中后,没有特殊的说明,那么该进程的所有子进程都将被纳入到Job里。当然,通过JOB_OBJECT_LIMIT_BREAKAWAY_OK或者JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK。Job内进程的子进程就可以不被自动纳入到Job中,两者区别在于,后者所有子进程自动的赶出Job,而前者的子进程需要在CreateProcess时指定CREATE_BREAKAWAY_FROM_JOB

如果Job被强制关闭,那么Job内的所有进程都将被终止(Terminate)

Job所指定的CPU时间耗光后,Job HANDLE将处于Signaled状态,Wait系列函数就可以响应这个事件。

此外,任何Job中重要的事件,例如新进程的产生和终止,进程CPU时间的耗尽等等都可以通过完成端口的形式获得通知。(第五章第五小节)

---------------------------------------------------

实战:

为一个Job增加两个限制

1.不允许使用ExitWindow函数。

2.不能访问Job外进程的句柄。


我之前写过两个程序,第一是通过提升自己进程的权限实现关机,第二个是高权限的进程更改低权限进程的标题栏。那么我现在写一个Job,看看能否限制这两个进程。

首先是主程序,其负责创建一个Job和一个IO完成端口,完成端口来获取Job发来的通知

  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     HANDLE hJob = NULL;  
  4.     JOBOBJECT_BASIC_UI_RESTRICTIONS tagUIRestriction;  
  5.     tagUIRestriction.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE;  
  6.     tagUIRestriction.UIRestrictionsClass = JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_HANDLES;  
  7.     hJob = ::CreateJobObject(NULL,NULL); //创建Job  
  8.     if(!hJob)  
  9.     {  
  10.         printf_s("Job is not created\n");  
  11.         return GetLastError();  
  12.     }  
  13.     if(!SetInformationJobObject(hJob,JOBOBJECTINFOCLASS::JobObjectBasicUIRestrictions,&tagUIRestriction,sizeof(tagUIRestriction))) //设定UI限制  
  14.     {  
  15.         printf_s("Set UI Restriction Failed\n");   
  16.         CloseHandle(hJob);  
  17.         return GetLastError();  
  18.     }  
  19.     HANDLE hIOPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,1,0);  
  20.     if(!hIOPort)  
  21.     {  
  22.         printf_s("Create IO port Failed\n");  
  23.         CloseHandle(hJob);  
  24.         return GetLastError();  
  25.     }  
  26.     JOBOBJECT_ASSOCIATE_COMPLETION_PORT tagIOPort;  
  27.     tagIOPort.CompletionKey = (PVOID)1;  
  28.     tagIOPort.CompletionPort = hIOPort;  
  29.     if(!SetInformationJobObject(hJob,JOBOBJECTINFOCLASS::JobObjectAssociateCompletionPortInformation,&tagIOPort,sizeof(tagIOPort))) //关联完成端口  
  30.     {  
  31.         printf_s("Associate IO Port Failed\n");  
  32.         CloseHandle(hJob);  
  33.         CloseHandle(hIOPort);  
  34.         return GetLastError();  
  35.     }  
  36.     HANDLE hThread = CreateThread(NULL,0,ThreadForAllProcessTerminated,hIOPort,NULL,NULL);  
  37.     CreateProcessForJob(_T("AdjustPrivilegeToShutDown.exe"),hJob); //创建进程  
  38.     CreateProcessForJob(_T("ProcessWithHighPrevilige.exe"),hJob);//创建进程  
  39.     WaitForSingleObject(hThread,INFINITE);  
  40.     CloseHandle(hJob);  
  41.     return 0;  
  42. }  

其次是创建进程的函数,需要注意的是在创建进程的时候我使用了CREATE_BREAKAWAY_FROM_JOB,这是因为从Vista开始,从Explorer启动的程序有时候会被自动放在一个PCA开头的Job里,传说这个Job是用来检测程序兼容性的,为了能把新进程放到别的Job中,我自然要把新进程从这个Job中先脱离出去。

  1. BOOL CreateProcessForJob(LPCTSTR pszCmdLine,HANDLE hJob)  
  2. {  
  3.     STARTUPINFO si = {0};  
  4.     si.cb = sizeof(si);  
  5.     PROCESS_INFORMATION tagPI;  
  6.     TCHAR pszCommandLine[MAX_PATH];  
  7.     if (FAILED(StringCchCopy(pszCommandLine,MAX_PATH,pszCmdLine)))  
  8.     {  
  9.         _tprintf_s(_T("Copy process name %s failed\n"),pszCmdLine);  
  10.         return FALSE;  
  11.     }  
  12.     if(!CreateProcess(NULL,pszCommandLine,NULL,NULL,FALSE,CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,NULL,NULL,&si,&tagPI)) //Braak from the creator's job and assign it to a new one  
  13.     {  
  14.         _tprintf_s(_T("Create process %s failed\n"),pszCmdLine);  
  15.         return FALSE;  
  16.     }  
  17.     if(!AssignProcessToJobObject(hJob,tagPI.hProcess))  
  18.     {  
  19.         TerminateProcess(tagPI.hProcess,0);  
  20.         CloseHandle(tagPI.hProcess);  
  21.         CloseHandle(tagPI.hThread);  
  22.         _tprintf_s(_T("Assign process %s to job failed\n"),pszCmdLine);  
  23.         return FALSE;  
  24.     }  
  25.     ::ResumeThread(tagPI.hThread);  
  26.     CloseHandle(tagPI.hProcess);  
  27.     CloseHandle(tagPI.hThread);  
  28.     return TRUE;  
  29. }  
最后是处理Job通知的线程

  1. DWORD WINAPI ThreadForAllProcessTerminated(LPVOID pParam)  
  2. {  
  3.     HANDLE hIOPort = (HANDLE)pParam;  
  4.     DWORD dwMsg = 0;  
  5.     ULONG_PTR idKey = 0;  
  6.     DWORD dwProcessID;  
  7.     BOOL bGoOn = TRUE;  
  8.     while(bGoOn && GetQueuedCompletionStatus(hIOPort,&dwMsg,&idKey,(LPOVERLAPPED*)&dwProcessID,INFINITE))  
  9.     {  
  10.         if (idKey == 1)  
  11.         {  
  12.             switch(dwMsg)  
  13.             {  
  14.             case JOB_OBJECT_MSG_EXIT_PROCESS:  
  15.                 printf("A process ended\n");  
  16.                 break;  
  17.             case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:  
  18.                 printf("All processes ended\n");  
  19.                 bGoOn = FALSE;  
  20.                 break;  
  21.             }  
  22.         }  
  23.     }  
  24.     return 0;  
  25. }  

这样,自动关机和修改低权限窗口的操作都无法进行了,Job有效地限制了其中进程的行为

有关PCAXXX的Job,书里有这样一段话

Caution

By default, when you start an application through Windows Explorer, the process gets automatically associated to a dedicated job, whose name is prefixed by the "PCA" string. As you will see in "Job Notifications" on page 140, it is possible to receive a notification when a process in a job exits. So, when a legacy application started by Windows Explorer appears to fail, the Program Compatibility Assistant is triggered.

If your application needs to create a job like the Job Lab program presented at the end of this chapter, you are out of luck because this creation will fail because of the "PCA"-prefixed job object that is already associated with your process.

This feature is provided by Windows Vista only to detect compatibility issues. So, if you have defined a manifest for your application as explained inChapter 4, "Processes," Windows Explorer won't attach your process to the "PCA"-prefixed job, assuming that you have already fixed any possible compatibility problem.

However, when you need to debug your application, if the debugger has been started from Windows Explorer, even with a manifest, your application will inherit the "PCA"-prefixed job from the debugger. One easy solution is to start the debugger from a command shell instead of from Windows Explorer. In that case, the job association does not occur.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值