【第五章】作业
什么是作业
在Windows操作系统中,作业是一种系统提供的机制,用于对一组进程进行管理和控制。作业Job是通过作业对象(内核对象)来实现的。通常,作业对象在操作系统中用于对多个进程进行统一管理。比如资源控制、杀死作业中的?进程等。
本质上,作业对象就是一个容器,用于存放多个进程。通过作业对象,我们可以对这些多个进程进行统一的管理。
给出下面例子来帮助理解作业。
比如,vs在构建一个c++项目时会生成Cl.exe(C和C++编译器),后者会生成很多其它的进程(编译器每次对资源文件进行扫描的时候)。反正就是构建一个c++项目时会需要生成多个进程。如果用户希望提前终止构建,就需要这些进程全部停止,包括Cl.exe及其子进程。又由于Windows没有维护进程父子关系,这个问题就变得很麻烦。有了作业,每生成一个相关进程就将其加入到某个作业中,通过这个作业对象来进行统一的终止。
作业对象相关函数
CreateJobObject
- 用途:创建一个新的作业对象
- 函数原型:
HANDLE CreateJobObject(
LPSECURITY_ATTRIBUTES lpJobAttributes,
LPCWSTR lpName
);
- 参数信息:
lpJobAttributes
: 指向一个 SECURITY_ATTRIBUTES 结构体的指针,用来指定作业对象的安全性(可以设置为 NULL,表示默认安全设置)。lpName
:作业对象名称,可以为NULL,如果指定了名称(唯一),则可以通过名称来打开作业对象- 返回值:如果成功,返回作业对象句柄,否则为NULL,并使用GetLastError获取错误代码。
HANDLE hJob = CreateJobObject(NULL, NULL);
if (hJob == NULL) {
// 错误处理
DWORD dwError = GetLastError();
}
AssignProcessToJobObject
- 用途:将进程附加到作业对象
- 原型:
BOOL AssignProcessToJobObject(
HANDLE hJob,
HANDLE hProcess
);
- 参数:
hJob
:作业对象句柄hProcess
:要附加到作业对象的进程的句柄- 返回值:成功附加则返回TRUE,失败返回FALSE,并可以使用GetLastError获取错误码。
- 示例:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (AssignProcessToJobObject(hJob, hProcess)) {
// 进程成功附加到作业对象
} else {
// 错误处理
}
SetInformationJobObject
- 用途:设置作业对象属性,如资源限制、作业控制等。
- 原型:
BOOL SetInformationJobObject(
HANDLE hJob,
JOBOBJECTINFOCLASS JobObjectInformationClass,
LPVOID lpJobObjectInformation,
DWORD cbJobObjectInformationLength
);
- 参数:
hJob
:作业对象句柄JobObjectInformationClass
:要设置的作业信息类别,比如:JobObjectBasicLimitInformation 用于设置资源限制lpJobObjectInformation
:指向包含作业信息的缓冲区cbJobObjectInformationLength
:缓冲区的大小- 返回值:如果成功,返回TRUE,否则为FALSE,并使用 GetLastError 获取错误代码。
- 示例:
JOBOBJECT_BASIC_LIMIT_INFORMATION jobLimits = {0};
jobLimits.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
jobLimits.ProcessMemoryLimit = 100 * 1024 * 1024; // 设置内存限制为100MB
JOBOBJECT_BASIC_UI_RESTRICTED_INFORMATION uiInfo = {0};
SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &jobLimits, sizeof(jobLimits));
QueryInformationJobObject
- 查询作业对象的属性
- 原型:
BOOL QueryInformationJobObject(
HANDLE hJob,
JOBOBJECTINFOCLASS JobObjectInformationClass,
LPVOID lpJobObjectInformation,
DWORD cbJobObjectInformationLength,
LPDWORD lpReturnLength
);
- 参数:
hJob
:作业对象的句柄JobObjectInformationClass
:要查询的信息类型lpJobObjectInformation
:用于接收查询结果的缓冲区cbJobObjectInformationLength
:缓冲区字节大小lpReturnLength
:实际接收到的字节数- 返回值:如果成功,返回TRUE,否则为FALSE,并可以使用 GetLastError 获取错误代码。
TerminateJobObject
- 用途:终止作业对象中的所有进程
- 原型:
BOOL TerminateJobObject(
HANDLE hJob,
UINT uExitCode
);
- 参数:
- hJob:作业对象的句柄
- uExitCode:是一个 UINT 类型的参数,表示当作业对象中所有的进程被终止时,它们会返回的退出代码
- 返回值:同上
作业的特性和功能
上面我们已经知道了什么是作业,下面来更为详细地说说作业的特性和功能。在此之前,我们需要知道,怎么创建一个作业。
进程资源限制
作业对象允许你限制作业中的所有进程的资源使用。例如,作业对象可以限制作业中的进程能够使用的内存量、CPU时间、句柄数等。当一个进程超出这些限制时,操作系统可以采取行动,比如终止该进程或将其挂起。
- 内存限制:可以设置作业的内存使用上限,防止某些进程消耗过多内存影响其它进程的运行。
- CPU时间限制:可以限制作业中所有进程使用的CPU时间。当进程没有达到指定的时间限制时,可以被暂停或终止
- 句柄限制:可以限制作业中进程所能打开的文件句柄或其它资源句柄的数量
进程的生命周期管理
作业对象允许你对作业中的进程进行生命周期管理。通过作业对象,可以执行以下操作:
- 终止作业中的进程:如果作业中的某个进程出错或者需要停止,作业对象可以确保所有进程统一终止。
- 暂停和恢复进程:作业对象可以将所有进程暂停,等到适当的时候恢复执行
- 监控作业进程状态:可以通过作业对象查询进程的状态,了解进程是否正常运行,是否已经完成,或者是否发生了异常。
作业对象与进程的关系
每个进程都可以附加到一个或者多个作业对象中。当进程附加到作业对象之后,系统将其纳入该作业的管理范围。作业对象本身并不创建进程,而是用于管理已存在的进程。
作业对象的事件与通知
作业对象还支持进程的结束事件。可以为作业对象设置事件,当作业中的所有进程都结束时,这些事件将被触发。这种机制可以用于等待多个进程完成工作。
作业对象与进程的优先级控制
可以通过作业对象对作业中的进程进行优先级控制。例如,限制作业中的进程只能在低优先级下运行,避免它们占用过多的系统资源影响其他更重要的进程。
使用作业对象的场景
- 限制资源的使用
假设有一个程序会启动多个子进程,但担心某个子进程可能会占用太多内存或CPU,影响整个系统。你可以将这些子进程加入到一个作业对象中,然后为整个作业设定内存或CPU的使用上限。当某个进程尝试超出这个限制时,系统就能及时干预,保证其他进程不受影响 - 统一终止多个进程
想象一个复杂的应用程序由多个独立的进程组成,例如,一个浏览器可能包含主进程、渲染进程、插件进程等。如果用户选择关闭浏览器,通过作业对象可以一键终止所有相关进程,确保没有进程残留在后台继续占用资源 - 错误恢复和安全保护
在一些服务器或后台任务中,为了防止某个进程异常崩溃导致整个服务出问题,可以将相关进程放到一个作业对象中。如果发现其中一个进程出现问题,系统可以迅速终止整个作业,并重新启动所有进程,保持服务的整体稳定性