window的job(作业) 用于管理一个进程集合,是job中的所有进程执行相同的操作。
- 作业可以看成一个进程池;Job
- 一个进程一旦加入一个作业,就不能再加入另外一个作业。
- 一个进程一旦加入一个作业,就不能再退出来。
- 作业也是一个内核对象。
- 如果一个进程加入了一个作业,那么这个进程使用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简介
创建一个作业
//创建一个job
HANDLE hJob1 = CreateJobObject(NULL, L"作业名字");
_tprintf(L"hJob1 = %X GetLastError = %d\n", hJob1, GetLastError());
创建一个进程
//初始化进行相关参数
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(L"x32.exe", // No module name (use command line)
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
NULL, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
_tprintf(L"CreateProcess failed (%d).\n", GetLastError());
return 0;
}
将进程加入到作业中
将进程加入到作业中的函数是AssignProcessToJobObject
该函数有两个参数一个是hJob:要将进程hProcess加入的那个作业的句柄,另一个是Process:要加入到作业中的进程句柄
返回值为 return:成功返回1,否则返回0
/*
BOOL WINAPI AssignProcessToJobObject(
__in HANDLE hJob,
__in HANDLE hProcess
);
hJob: 要将进程hProcess加入的那个作业的句柄
Process: 要加入到作业中的进程句柄
return: 成功返回1,否则返回0
*/
//将进程添加到job中
re = AssignProcessToJobObject(hJob1, GetCurrentProcess());
_tprintf(L" re = %d \n", re);
检查进程是否已经存在于作业中IsProcessInJob
IsProcessInJob 函数包括三个参数
ProcessHandle:需要检查确认的进程句柄 A。
JobHandle:作业句柄 B
Result:检查结果,如果是1,那么说明进程A已经被放入做也B,否则进程A没有被放到作业B中。
Return:成功,返回1,否则返回1
re = IsProcessInJob(GetCurrentProcess(), NULL, &Result); //检查进程是否在job中
_tprintf(L" re = %d Result = %d\n", re, Result);
完整的代码
#include<Windows.h>
#include<tchar.h>
int _tmain()
{
/*
判断一个进程是否已经加入了一个作业的函数是:IsProcessInJob;
BOOL WINAPI IsProcessInJob(
__in HANDLE ProcessHandle,
__in_opt HANDLE JobHandle,
__out PBOOL Result
);
ProcessHandle: 要判断的进程句柄;
JobHandle: 看看进程是否加入到JobHandle这个作业中;
Result: 判断结果。
*/
//创建一个job
HANDLE hJob1 = CreateJobObject(NULL, L"hello");
_tprintf(L"hJob1 = %X GetLastError = %d\n", hJob1, GetLastError());
HANDLE hJob2 = CreateJobObject(NULL, L"hello");
_tprintf(L"hJob2 = %X GetLastError = %d\n", hJob2, GetLastError());
/*
BOOL WINAPI IsProcessInJob(
__in HANDLE ProcessHandle,
__in_opt HANDLE JobHandle,
__out PBOOL Result
);
ProcessHandle 需要检查确认的进程句柄 A。
JobHandle 作业句柄 B
Result 检查结果,如果是1,那么说明进程A已经被放入做也B,否则进程A没有被放到作业B中。
Return 成功,返回1,否则返回1
*/
DWORD re;
BOOL Result;
//检查进程是否在job中
re = IsProcessInJob(GetCurrentProcess(), NULL, &Result); //检查进程是否在job中
_tprintf(L" re = %d Result = %d\n", re, Result);
/////////////////////////////////////////////////////////
//初始化进行相关参数
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
/*
BOOL WINAPI AssignProcessToJobObject(
__in HANDLE hJob,
__in HANDLE hProcess
);
hJob: 要将进程hProcess加入的那个作业的句柄
Process: 要加入到作业中的进程句柄
return: 成功返回1,否则返回0
*/
//将进程添加到job中
re = AssignProcessToJobObject(hJob1, GetCurrentProcess());
_tprintf(L" re = %d \n", re);
re = IsProcessInJob(GetCurrentProcess(), NULL, &Result);
_tprintf(L" re = %d Result = %d\n", re, Result);
// Start the child process.
if (!CreateProcess(L"x32.exe", // No module name (use command line)
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
NULL, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
_tprintf(L"CreateProcess failed (%d).\n", GetLastError());
return 0;
}
/////////////////////////////////////////
re = IsProcessInJob(pi.hProcess, hJob1, &Result);
_tprintf(L" re = %d pi.hProcess Result = %d\n", re, Result);
_gettchar();
CloseHandle(hJob1);
CloseHandle(hJob2);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}