第五章:作业

C++ 同时被 3 个专栏收录
84 篇文章 0 订阅
29 篇文章 0 订阅
14 篇文章 0 订阅

第五章的笔记我做了很长时间,主要是最近很忙,前不久去了南京出差.今天把东西补上.

 1.Windows提供了一个作业内核对象,他允许我们将进程组合在一起并创建一个"沙箱"来限制进程能够做什么,最好将作业对象想象成一个进程容器,但是创建一个只包含单个进程的作业同样非常有用(这样一来我们尽可以对它进行限制).

2.函数解释:
◆BOOL isProcessInJob(
HANDLE hProcess, //进程句柄
HANDLE hJob, //作业句柄
PBOOL  pbInJob); //返回值,表明进程是否在当前的作业中
注:如果进程已与一个作业关联,近无法将当前进程或者他的任何子进程从作业中去除.这个安全特性确保进程无法摆脱对它的限制)
另外,windows Vista中通过windows资源管理器来启动一个应用程序时,进程会自动同一个专用的作业关联,此作业的名称使用了"PCA"字符串前缀.作业中的一个进程退出时,我们是可以收到一个通知的.所以一旦通过windows资源管理器启动的一个老版本的应用程序出现问题,就会触发Program Compatibility Assistant(程序兼容性助手).所以我们尽量是使用命令行来启动应用程序.
◆ CreateJobObject(
 PSECURITY_ATTRIBUTES psa,//安全属性对象
 PCTSTR    pszName)//命名作业对象,使其能够由OperJobObject访问
◆ HANDLE OpenJobObject(
DWORD dwDesiredAccess,//以何种方式来打开作业
BOOL   bInheritHandle,//作业
PCTSTR pszName)
3.作业创建好以后,就可以对作业里的进程施加限制,一般有如下几种:
1).基本限额和拓展基本限额,用于防止作业中的进程独占系统资源.
2).基本UI限制,用于防止作业内的进程更改用户界面
3).安全限额,用于防止作业内的进程访问安全资源(文件,注册表子项等)
主要的函数有:
BOOL SetInformationJobObject(
HANDLE hJob,//作业句柄

JOBOBJECTINFOCLASS JobObjectInformationClass,//指定哪种限制

PVOID pJobObjectInformation,//具体的限制
DWORD  cbJobObjectInformationSize)//PVOID结构的大小

限制的类型:

限制类型

第二个参数的值

第三个参数所对应的数据结构

基本限额

JobObjectBasicLimitInformation

JOBOBJECT_BASIC_LIMIT_INFORMATION

拓展后的基本限额

JobObjectExtendedLimitInformation

JOBOBJECT_EXTENDED_LIMIT_INFORMATION

基本的UI限额

JobObjectBasicUIRestrictions

JOBOBJECT_BASIC_UI_RESTRICTIONS

安全限额

JobObjectSecurityLimitInformation

JOBOBJECT_SECURITY_LIMIT_INFORMATION

各个结构定义如下:

●JOBOBJECT_BASIC_LIMIT_INFORMATION结构备注:

■ PerProcessUserTimeLimit:对于占用时间超过其分配时间的任何进程,系统将自动终止他的运行.要设置看这个限额必须指定LimitFlags为JOB_OBJECT_LIMIT_PROCESS_TIME

■ PerJobUserTimeLimit:默认情况下,在达到改时间限额时,系统将自动终止所有进程的运行.可以在作业运行定期改变这个值.同样要设置JOB_OBJECT_JOB_TIME标志

■ LimitFlags:指定将哪些限制应用于作业

■ MinimumWorkingSetSize,MaximumWorkingSetSize:正常情况下,进程工作集能扩展至最大值以上,设置了MaximumWorkingSetSize后,就可以对其设置硬性的限额,一旦进程的工作集抵达这个限额,进程就会开始进行换页操作.除非进程只是尝试清空它的工作集.否则则会忽略SetProcessWorkingSetSize的调用,需设置JOB_OBJECT_LIMIT_WORKINGSET标志

■ ActiveProcessLimit:超过此限额的任何尝试都会导致新进程终止,并报告一个配额不足错误,需要设置JOB_OBJECT_LIMIT_ACTIVE_PROCESS标志.

■ Affinity:单独的进程可以进一步对此进行限定.需设置JOB_OBJECT_AFFINITY标志

■ PriorityClass:如果一个进程调用SetPriorityClass函数,即使该函数调用失败,也会成功返回.如果进程调用GetPriorityClass函数,该函数将返回进程已经设置的优先级类,尽管这可能并不是进程实际的优先级类.此外SetPriorityClass不可以提高到Normal以上,但可以调低优先级.设置标志JOB_OBJECT_LIMIT_PRIORITY_CLASS标志

■ SchedulingClass:值可以再0~9之间(闭区间)默认是5需设置JOB_OBJECT_LIMIT_SCHEDULING_CLASS标志.值越大,表明分配的时间越长.但是也要避免使用很大的数字(这样的话就会使系统其他的进程反应越来越迟钝)

备注:如果我们想在一个已经设置了JOB_OBJECT_LIMIT_JOB_TIME和JOB_OBJECT_LIMIT_PRIORITY_CLASS标志的作业里改变作业的关联性,又想保留现有的CPU时间限制,只是不希望扣除已终止进程的CPU时间统计信息.可以设置JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME(这个标志指出我们希望在改变限制条件的同时,不扣除已终止运行的那些进程的CPU时间统计信息.因为作业在运行过程中,会维护一些统计信息[包括作业中的进程使用了多少CPU时间],这样,在设置时间限制时,他会扣除已终止线程的时间).

还有一个标志位:JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION限额标志,这个标志将会导致系统关闭与作业关联的每一个进程的"未处理的异常对话框",为此,系统会为作业中的每一个进程调用SetErrorMode函数,并向他传递SEM_NOGPFAULTERRORBOX标志.

● JOBOBJECT_EXTENDED_LIMIT_INFORMATION结构:

● JOB_OBJECT_BASIC_UI_RESTRICTIONS结构:

UIRestrictionClass的取值:

标志

描述

JOB_OBJECT_UILIMIT_EXITWINDOWS

阻止进程通过ExitWindowsEx函数注销、关机、重启或断开系统电源

JOB_OBJECT_UILIMIT_READCLIPBOARD

阻止进程读取剪切板中的内容

JOB_OBJECT_UILIMIT_WRITECLIPBOARD

阻止进程清除剪切板中的内容

JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS

阻止进程通过SystemParametersInfo函数更改系统参数

JOB_OBJECT_UILIMIT_DISPLAYSETTINGS

阻止进程通过ChangeDisplaySetting函数更改显示设置

JOB_OBJECT_UILIMIT_GLOBALATOMS

为作业指定其专有的全局原于表,并限制作业中的进程只能访问此作业的表

JOB_OBJECT_UILIMIT_DESKTOP

阻止进程使用CreateDesktop或者SwitchDesktop函数来创建或切换桌面

JOB_OBJECT_UILIMIT_HANDLES

阻止作业中的进程使用同一个作业外部的进程所创建的用户对象(比如HWND),这样就意味着作业中的任何一个进程都不能访问作业外部进程所创建的用户对象.

如果想要想要让作业内部的一个进程同作业外的一个进程通讯,则可以使用UserHandleGrantAccess函数来解决这个问题.

BOOL UserHandleGrantAccess(

HANDLE hUserObj,//指定一个用户对象,如果我们想允许或拒绝作业内部的进程访问 //此对象,这几乎总是一个窗口句柄,但也可以是其他用户对象

HANDLE hJob, //授权那个作业访问

BOOL   bGrant); //拒绝那个作业访问

注意:如果从hJob所标示的作业内的一个进程内调用这个函数,函数调用会失败.(主要是为了防止对自身授权).

4. 查询作业施加的限制:
BOOL QueryInformationJobObject(
HANDLE hJob, //指出我们希望有哪些限制信息
JOBOBJECTINFOCLASS JobObjectInformationClass,//指出我们希望的限制信息
PVOID pvJobObjectInformation, //包含改数据结构的地址
DWORD cbJobObjectInformationSize,//包含该数据结构的数据块的大小
PDWORD pdwReturnSize); //指出由此函数来填充的一个DWORD,指出缓 //冲区中已填充了多少个字节.

5. 函数:
● 将某个进程显示的放入我新建的作业中
BOOL AssignProcessToJobObject(
HANDLE hJob, //作业句柄
HANDLE hProcess);//进程句柄
注:这个函数向系统表明此进程当做现在作业的一部分.这个函数只允许将尚未分配给任何作业的一个进程分配给一个作业.
● BOOL IsProcessInJob (

   HANDLE ProcessHandle, //指向被测试的进程句柄

    HANDLE JobHandle, //作业句柄

   PBOOL Result );//返回值,为true标示进程属于作业.
当作业中的一个进程生成了另一个进程的时候,新进程将自动成为父进程所属于的作业的一部分.可以通过以下方式改变这种行为:

■ 打开JOBOBJECT_BASIC_LIMIT_INFORMATION的LimitFlags成员的JOB_OBJECT_LIMIT_BREAKAWAY_OK标志.告诉系统新生成的进程可以在作业外部执行.为此需要在调用CreateProcess函数时指定新的CREATE_BREAKAWAY_FROM_JOB标志,如果这样做了,但是作业并没有打开JOB_OBJECT_LIMIT_BREAKAWAY_OK限制,则会创建失败.

■  打开JOBOBJECT_BASIC_LIMIT_INFORMATION的LimitFlags成员JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK标志,此标志告诉系统新生成的子进程不应该是作业的一部分.现在就没有必要向CrateProcess传递任何标志

6. 函数说明:
■ 杀死作业内部的所有进程:
BOOL TerminateJobProject(
HANDLE hJob,
UINT   uExitCode);//作业退出码
■ 获取作业的基本统计信息:
BOOL QueryInformationJobObject(
HANDLE hJob, //指出我们希望有哪些限制信息
JOBOBJECTINFOCLASS JobObjectInformationClass,//指出我们希望的限制信息
PVOID pvJobObjectInformation, //包含改数据结构的地址
DWORD cbJobObjectInformationSize,//包含该数据结构的数据块的大小
PDWORD pdwReturnSize); //指出由此函数来填充的一个DWORD,指出缓 //冲区中已填充了多少个字节.
其中像第二个参数传递JobObjectBasicAccountingInformation和一个JOBOBJECT_BASIC_ACCOUNTING_INFORMATION结构的地址:


除了查询基本的统计信息外,还可以执行一个调用来同时查询基本统计信息和I/O统计信息.此时上述结构要改成JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION.


如果想要获取未放入作业的那些进程的信息则应使用GetProcessIoCounters:
BOOL GetProcessIoCounters(
HANDLE hProcess,
PIO_COUNTERS pIoCounters)
获取作业中当前的进程ID集:
typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST{
DWORD NumberOfAssignedProcess,
DWORD NumberOfProcessIdsInList,
DWORD ProcessIdList[1];
}JOBOBJECT_BASIC_PROCESS_ID_LIST,*PJOBOBJECT_BASIC_PROCESS_ID_LIST;
其一般步骤为:

void EnumProcessIdsInJob(HANDLE hjob) {

// I assume that there will never be more

// than 10 processes in this job.

#define MAX_PROCESS_IDS 10

// Calculate the number of bytes needed for structure & process IDs.

DWORD cb = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) +

(MAX_PROCESS_IDS – 1) * sizeof(DWORD);

// Allocate the block of memory.

PJOBOBJECT_BASIC_PROCESS_ID_LIST pjobpil =

(PJOBOBJECT_BASIC_PROCESS_ID_LIST)_alloca(cb);

// Tell the function the maximum number of processes

// that we allocated space for.

pjobpil->NumberOfAssignedProcesses = MAX_PROCESS_IDS;

// Request the current set of process IDs.

QueryInformationJobObject(hjob, JobObjectBasicProcessIdList,

pjobpil, cb, &cb);

// Enumerate the process IDs.

for (DWORD x = 0; x < pjobpil->NumberOfProcessIdsInList; x++) {

// Use pjobpil->ProcessIdList[x]...

}

// Since _alloca was used to allocate the memory,

// we don’t need to free it here.

}
7. 如果我们想监视某个进程是否已经完成,我们就可以利用完成端口来处理.
即:
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;

joacp.CompletionKey = 1;

joacp.CompletionPort = hIOCP;

SetInformationJobObject( hJob,JobObjectAssociateCompletionPortInformation,

&joacp,sizeof( joacp ));
此时系统将监视作业,只要有事情发生,就会把他们投递到I/O完成端口.通过调用GetQueuedCompletionStatus来监视完成端口:
BOOL GetQueuedCompletionStatus(
HANDLE hIOCP,

PDWORD pNumBytesTransferred,

PULONG_PTR pCompletionKey,//当函数返回一个作业通知时,返回完成的键

POVERLAPPED *pOverlapped,

DWORD dwMilliseconds);
键的值和事件:

事件

描述

JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO

作业中没有进程在运行时,就投递通知

JOB_OBJECT_MSG_END_OF_PROCESS_TIME

进程已分配的CPU时间到期时就投递此通知,进程将运行终止,并给出进程ID

JOB_OBJECT_MSG__ACTIVE_PROCESS_LIMIT

试图超过作业中的活动进程数时,就投递通知

JOB_OBJECT_MSG__PROCESS_MEMORY_LIMIT

进程试图调拨的存储超过进程的限额时,就投递同时给出进程ID

JOB_OBJECT_MSG_JOB_MEMORY_LIMIT

进程调拨的存储超过作业的限额时,就投递此通知,同时给出进程ID

JOB_OBJECT_MSG_NEW_PROCESS

一个进程添加到一个作业时,投递通知,给出ID

JOB_OBJECT_MSG_EXIT_PROCESS

一个进程终止运行时,投递通知.给出ID

JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS

一个进程由于为处理的异常而终止运行时投递,同时给出进程ID

JOB_OBJECT_MSG_END_OF_JOB_TIME

作业分配的CPU时间到期时,就投递,但其中的进程不会自动终止.我们可以运行进程继续运行.可以设置一个新的时间限制,还可以调用TerminateJobObject

  • 2
    点赞
  • 1
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

yuanweihuayan

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值