作业通知
一.作业(job)内核对象概念
1.为什么要有作业job
便于管理进程:进程的父子关系只存在于创建的子进程的那一刻,Windows并不一直维护着这种父子关系,这使得管理进程并不是件容易的事。
2.作业的功能
作业对象是用于将一组进程作为一个管理单元的内核对象,本质上可以理解为其实就是进程池对象,可将作业对象看作是进程的容器。
作来对象可以用来限制一组进程的占用内存数量、占用CPU周期数、进程优先级等的一个“沙箱”。
最终可以通过作业对象将该对象中的所有进程全部关闭(普通方法很难控制)
3.作业通知
通过结合一个完成端口对象并利用一个线程 实时动态的监控作业对象的执行(如得到一些消息,以便及时响应作业对象中的进程变化情况)
二.作业对象的基本用法
CreateJobObject (创建作业对象)
IsProcessInJob (进程是否己经与某个作业对象关联)
SetInformationJobObject (设置作业对象或进程的限制)
AssignProcessToJobObject (将进程添加到作业中)
QueryInformationJobObject (查询作业对象中施加的限制)
TerminateJobObject ("杀死"作业中所有的进程)
CloseHandle (关闭作业对象句柄,导致所有进程不能访问作业对象,但作业仍存在!)
OpenJobObject (打开一个指定名称的作业对象句柄)
三.作业通知实现
1.创建作业和完成端口:
//创建作业
HANDLE JobHandle = CreateJobObject(
NULL, //安全属性默认
NULL); //匿名作业
if (JobHandle == NULL)
{
goto Exit;
}
//创建一个完成端口
__CompletionPortHandle = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, //关联的文件句柄
NULL, //已经存在的完成端口
0, //传送给处理函数的参数
0); //有多少个线程在访问这个消息队列
if (__CompletionPortHandle==NULL)
{
goto Exit;
}
2.作业与完成关联
//作业与完成关联
JOBOBJECT_ASSOCIATE_COMPLETION_PORT AssociateCompletePort;
AssociateCompletePort.CompletionKey = (PVOID)JOB_OBJECT_COMPLETE_KEY;
AssociateCompletePort.CompletionPort = __CompletionPortHandle;
if (SetInformationJobObject(
JobHandle, //作业句柄
JobObjectAssociateCompletionPortInformation,
&AssociateCompletePort,
sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT)
)==FALSE)
{
goto Exit;
}
3.设置限制:
//UI限制
JOBOBJECT_BASIC_UI_RESTRICTIONS Restrictions;
Restrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; //初始化为0
Restrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; //进程不能访问用户对象(如窗口句柄)
Restrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; //进程不能关闭系统
//设置限制
SetInformationJobObject(
JobHandle, //作业对象句柄
JobObjectBasicUIRestrictions, //限制的类型,
&Restrictions, //指向初始化过的结构体,包含具体的限制
sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS)); //结构体的大小
4.创建一个守候在完成端口上的线程
//创建一个守候在完成端口上的线程,监视
HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcedure, NULL, 0, NULL);
if (ThreadHandle == NULL)
{
goto Exit;
}
源代码:
// 作业通知.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; #define JOB_OBJECT_COMPLETE_KEY 100 #define EXIT_THREAD_COMPLETE_KEY 101 DWORD WINAPI ThreadProcedure(LPVOID ParameterData); HANDLE __CompletionPortHandle = NULL; BOOL __IsLoop = TRUE; int main() { //创建作业 HANDLE JobHandle = CreateJobObject( NULL, //安全属性默认 NULL); //匿名作业 if (JobHandle == NULL) { goto Exit; } //创建一个完成端口 __CompletionPortHandle = CreateIoCompletionPort( INVALID_HANDLE_VALUE, //关联的文件句柄 NULL, //已经存在的完成端口 0, //传送给处理函数的参数 0); //有多少个线程在访问这个消息队列 if (__CompletionPortHandle==NULL) { goto Exit; } //作业与完成关联 JOBOBJECT_ASSOCIATE_COMPLETION_PORT AssociateCompletePort; AssociateCompletePort.CompletionKey = (PVOID)JOB_OBJECT_COMPLETE_KEY; AssociateCompletePort.CompletionPort = __CompletionPortHandle; if (SetInformationJobObject( JobHandle, //作业句柄 JobObjectAssociateCompletionPortInformation, &AssociateCompletePort, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT) )==FALSE) { goto Exit; } //UI限制 JOBOBJECT_BASIC_UI_RESTRICTIONS Restrictions; Restrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; //初始化为0 Restrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; //进程不能访问用户对象(如窗口句柄) Restrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES; //进程不能关闭系统 //设置限制 SetInformationJobObject( JobHandle, //作业对象句柄 JobObjectBasicUIRestrictions, //限制的类型, &Restrictions, //指向初始化过的结构体,包含具体的限制 sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS)); //结构体的大小 //创建一个守候在完成端口上的线程,监视 HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcedure, NULL, 0, NULL); if (ThreadHandle == NULL) { goto Exit; } WCHAR v1; do { wprintf(L"1..Create Process In Job\n"); wprintf(L"2..Remove Process From Job\n"); wprintf(L"3..Query Restrictions\n"); wprintf(L"4..Exit\n"); wscanf(L" %c", &v1); switch (v1) { case '1': { STARTUPINFO StartupInfo; memset(&StartupInfo, 0, sizeof(STARTUPINFO)); StartupInfo.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION ProcessInfo; memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION)); WCHAR CommandLine[] = L"Notepad.exe"; //创建进程,且传入CREATE_SUSPENDED标志,暂时挂起进程,避免进程逃离“沙箱” BOOL IsOk = CreateProcess(NULL,CommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED | CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &StartupInfo, &ProcessInfo); //将进程放入作业中 AssignProcessToJobObject(JobHandle, ProcessInfo.hProcess); //再调用ResumeThread ,使进程的线程可以在作业的限制下执行代码 ResumeThread(ProcessInfo.hThread); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); break; } case '2': { TerminateJobObject(JobHandle, 0); //终止作业中的所有进程 break; } case '4': { //发送自定义事件,触发GetQueuedCompletionStatus函数来取得数据包 PostQueuedCompletionStatus( __CompletionPortHandle, //完成端口对象 0, EXIT_THREAD_COMPLETE_KEY, //退出消息 NULL); goto Exit; } case '3': { QueryInformationJobObject(JobHandle, JobObjectBasicUIRestrictions, &Restrictions, sizeof(Restrictions), NULL); wprintf(L"%x\n", Restrictions.UIRestrictionsClass); } } wprintf(L"Do You Want To Continue?\n"); wscanf(L" %c", &v1); } while (v1 == 'y' || v1 == 'Y'); Exit: if (__CompletionPortHandle!=NULL) { CloseHandle(__CompletionPortHandle); __CompletionPortHandle = NULL; } if (JobHandle!=NULL) { CloseHandle(JobHandle); JobHandle = NULL; } if (ThreadHandle!=NULL) { WaitForSingleObject(ThreadHandle, INFINITE); CloseHandle(ThreadHandle); ThreadHandle = NULL; } printf("Input AnyKey To Exit\r\n"); getchar(); return 0; } DWORD WINAPI ThreadProcedure(LPVOID ParameterData) { DWORD TransferredDataLength = 0; ULONG_PTR CompletionKey = 0; LPOVERLAPPED Overlapped; BOOL IsOk = FALSE; while (__IsLoop) { IsOk = GetQueuedCompletionStatus( __CompletionPortHandle, &TransferredDataLength, //返回一个事件的ID &CompletionKey, //返回触发这个事件的对象的句柄,即作业对象的句柄 &Overlapped, //事件对应的详细信息(NULL) INFINITE); if (IsOk==TRUE) { if (CompletionKey == JOB_OBJECT_COMPLETE_KEY) { switch (TransferredDataLength) { /* 事件类型: JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS 进程异常退出 JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT 同时活动的进程数达到设置的上限 JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO 作业对象中没有活动的进程了 JOB_OBJECT_MSG_END_OF_JOB_TIME 作业对象的CPU周期耗尽 JOB_OBJECT_MSG_END_OF_PROCESS_TIME 进程的CPU周期耗尽 JOB_OBJECT_MSG_EXIT_PROCESS 进程正常退出 JOB_OBJECT_MSG_JOB_MEMORY_LIMIT 作业对象消耗内存达到上限 JOB_OBJECT_MSG_NEW_PROCESS 有新进程加入到作业对象中 JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT 进程消耗内存数达到上限 */ //作业通知 case JOB_OBJECT_MSG_NEW_PROCESS: { wprintf(L"New Process ID:%d In Job\n", Overlapped); break; } case JOB_OBJECT_MSG_EXIT_PROCESS: { wprintf(L"New Process ID:%d Out Job\n", Overlapped); //进程已经消亡 break; } case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: { wprintf(L"Active Procss Is Zero\n"); } default: { } } } else if (CompletionKey==EXIT_THREAD_COMPLETE_KEY) { printf("EXIT_THREAD_COMPLETE_KEY\r\n"); break; } } else { printf("CloseHandle(__CompletionPortHandle)\r\n"); break; } } printf("ThreadProcedure() Exit\r\n"); return 0; }