window的job(作业) 用于管理一个进程集合,是job中的所有进程执行相同的操作。

  1. 作业可以看成一个进程池;Job
  2. 一个进程一旦加入一个作业,就不能再加入另外一个作业。
  3. 一个进程一旦加入一个作业,就不能再退出来。
  4. 作业也是一个内核对象。
  5. 如果一个进程加入了一个作业,那么这个进程使用CreateProcess创建的所有子进程,自动加入到父进程加入的作业中。否则,调用CreateProcess函数的时候,需要在createprocess的flag参数设置为CREATE_BREAKAWAY_FROM_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之后,继续运行进程即可。

详见如下文档:Windows Job简介

创建一个作业

  1. //创建一个job
  2. HANDLE hJob1 = CreateJobObject(NULL, L"作业名字");
  3. _tprintf(L"hJob1 = %X GetLastError = %d\n", hJob1, GetLastError());

创建一个进程

  1. //初始化进行相关参数
  2. STARTUPINFO si;
  3. PROCESS_INFORMATION pi;
  4. ZeroMemory(&si, sizeof(si));
  5. si.cb = sizeof(si);
  6. ZeroMemory(&pi, sizeof(pi));
  7. // Start the child process.
  8. if (!CreateProcess(L"x32.exe", // No module name (use command line)
  9. NULL, // Command line
  10. NULL, // Process handle not inheritable
  11. NULL, // Thread handle not inheritable
  12. NULL, // Set handle inheritance to FALSE
  13. 0, // No creation flags
  14. NULL, // Use parent's environment block
  15. NULL, // Use parent's starting directory
  16. &si, // Pointer to STARTUPINFO structure
  17. &pi) // Pointer to PROCESS_INFORMATION structure
  18. )
  19. {
  20. _tprintf(L"CreateProcess failed (%d).\n", GetLastError());
  21. return 0;
  22. }

将进程加入到作业中

将进程加入到作业中的函数是AssignProcessToJobObject
该函数有两个参数一个是hJob:要将进程hProcess加入的那个作业的句柄,另一个是Process:要加入到作业中的进程句柄
返回值为 return:成功返回1,否则返回0

  1. /*
  2. BOOL WINAPI AssignProcessToJobObject(
  3. __in HANDLE hJob,
  4. __in HANDLE hProcess
  5. );
  6. hJob: 要将进程hProcess加入的那个作业的句柄
  7. Process: 要加入到作业中的进程句柄
  8. return: 成功返回1,否则返回0
  9. */
  10. //将进程添加到job中
  11. re = AssignProcessToJobObject(hJob1, GetCurrentProcess());
  12. _tprintf(L" re = %d \n", re);

检查进程是否已经存在于作业中IsProcessInJob

IsProcessInJob 函数包括三个参数
ProcessHandle:需要检查确认的进程句柄 A。
JobHandle:作业句柄 B
Result:检查结果,如果是1,那么说明进程A已经被放入做也B,否则进程A没有被放到作业B中。
Return:成功,返回1,否则返回1

  1. re = IsProcessInJob(GetCurrentProcess(), NULL, &Result); //检查进程是否在job中
  2. _tprintf(L" re = %d Result = %d\n", re, Result);

完整的代码

  1. #include<Windows.h>
  2. #include<tchar.h>
  3. int _tmain()
  4. {
  5. /*
  6. 判断一个进程是否已经加入了一个作业的函数是:IsProcessInJob;
  7. BOOL WINAPI IsProcessInJob(
  8. __in HANDLE ProcessHandle,
  9. __in_opt HANDLE JobHandle,
  10. __out PBOOL Result
  11. );
  12. ProcessHandle: 要判断的进程句柄;
  13. JobHandle: 看看进程是否加入到JobHandle这个作业中;
  14. Result: 判断结果。
  15. */
  16. //创建一个job
  17. HANDLE hJob1 = CreateJobObject(NULL, L"hello");
  18. _tprintf(L"hJob1 = %X GetLastError = %d\n", hJob1, GetLastError());
  19. HANDLE hJob2 = CreateJobObject(NULL, L"hello");
  20. _tprintf(L"hJob2 = %X GetLastError = %d\n", hJob2, GetLastError());
  21. /*
  22. BOOL WINAPI IsProcessInJob(
  23. __in HANDLE ProcessHandle,
  24. __in_opt HANDLE JobHandle,
  25. __out PBOOL Result
  26. );
  27. ProcessHandle 需要检查确认的进程句柄 A。
  28. JobHandle 作业句柄 B
  29. Result 检查结果,如果是1,那么说明进程A已经被放入做也B,否则进程A没有被放到作业B中。
  30. Return 成功,返回1,否则返回1
  31. */
  32. DWORD re;
  33. BOOL Result;
  34. //检查进程是否在job中
  35. re = IsProcessInJob(GetCurrentProcess(), NULL, &Result); //检查进程是否在job中
  36. _tprintf(L" re = %d Result = %d\n", re, Result);
  37. /////////////////////////////////////////////////////////
  38. //初始化进行相关参数
  39. STARTUPINFO si;
  40. PROCESS_INFORMATION pi;
  41. ZeroMemory(&si, sizeof(si));
  42. si.cb = sizeof(si);
  43. ZeroMemory(&pi, sizeof(pi));
  44. /*
  45. BOOL WINAPI AssignProcessToJobObject(
  46. __in HANDLE hJob,
  47. __in HANDLE hProcess
  48. );
  49. hJob: 要将进程hProcess加入的那个作业的句柄
  50. Process: 要加入到作业中的进程句柄
  51. return: 成功返回1,否则返回0
  52. */
  53. //将进程添加到job中
  54. re = AssignProcessToJobObject(hJob1, GetCurrentProcess());
  55. _tprintf(L" re = %d \n", re);
  56. re = IsProcessInJob(GetCurrentProcess(), NULL, &Result);
  57. _tprintf(L" re = %d Result = %d\n", re, Result);
  58. // Start the child process.
  59. if (!CreateProcess(L"x32.exe", // No module name (use command line)
  60. NULL, // Command line
  61. NULL, // Process handle not inheritable
  62. NULL, // Thread handle not inheritable
  63. NULL, // Set handle inheritance to FALSE
  64. 0, // No creation flags
  65. NULL, // Use parent's environment block
  66. NULL, // Use parent's starting directory
  67. &si, // Pointer to STARTUPINFO structure
  68. &pi) // Pointer to PROCESS_INFORMATION structure
  69. )
  70. {
  71. _tprintf(L"CreateProcess failed (%d).\n", GetLastError());
  72. return 0;
  73. }
  74. /////////////////////////////////////////
  75. re = IsProcessInJob(pi.hProcess, hJob1, &Result);
  76. _tprintf(L" re = %d pi.hProcess Result = %d\n", re, Result);
  77. _gettchar();
  78. CloseHandle(hJob1);
  79. CloseHandle(hJob2);
  80. CloseHandle(pi.hProcess);
  81. CloseHandle(pi.hThread);
  82. return 0;
  83. }