Loading... # Windows核心编程-学习笔记3 ## 作业 Windows提供了一个作业内核对象,允许我们将进程组合在一起并创建一个沙箱来限制进程能做什么 ### 对作业中的进程施加限制 限制有: * 基本限制和扩展基本限制,用于防止作业中的进程独占系统资源 * 基本UI限制,用于防止作业内进程更改用户界面 * 安全限制,用于防止作业内的进程访问安全资源 可调用SetInformationJobObject向作业施加限制 ~~~c BOOL SetInformationJobObject(HANDLE hJob, JOBOBJECTINFOCLASS JobObjectInformationClass, PWOID pJobObjectInformation, DWORD cbJobObjectInformationSize); ~~~ 第一个参数指定要限制的作业(不能传NULL来修改进程自身限制),第二个参数是一个枚举类型,指定了要施加的限制类型,第三个参数是一个数据结构的地址,包含具体的限制设置。第四个参数指出数据结构的大小用于版本控制 | 限制类型 | 第二个参数值 | 第三个参数对应的数据结构 | | ---------------- | --------------------------------- | ------------------------------------ | | 基本限制 | JobObjectBasicLimitInformation | JOBOBJECT_BASIC_LMIT_INFORMATION | | 扩展后的基本限制 | JobObjectExtendedLimitInformation | JOBOBJECT_EXTENDED_LMIT_INFORMATION | | 基本的UI限制 | JobObjectBasicUIRestrictions | JOBOBJECT_BASIC_UI_RESTRICTIONS | | 安全限制 | JobObejctSecurityLimitInformation | JOBOBJECT_SECURITY_LIMIT_INFORMATION | 来看下**基本限制** ~~~c typedef struct _JOBOBJECT_BASIC_LIMIT_INFORMATION { LARGE_INTERGER PerProcessUserTimeLimit; // 指定分配给每个进程最大用户模式时间 LARGE_INTERGER PerJobUserTimeLimit; // 闲置费配给作业对象的最大用户模式时间 DWORD LimitFlags; // 指定将哪些限制应用于作业 DWORD MinimumWorkingSetSize; // 指定每个进程的最小/大工作集 DWORD MaximumWorkingSetSize; DWORD ActiveProcessLimit; // 指定作业中能并发运行的进程的最大数量 DWORD_PTR Affinity; // 指定能够运行进程的CPU子集 DWORD PriorityClass; // 指定关联的所有进程的优先级类 DWORD SchedulingClass; // 为作业中的线程指定一个相对时间量差 } JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION; ~~~ SchedulingClass可以通过设置0-9来控制各个作业相对调度优先级,值越大越优先,默认是5 **扩展限制**:包含JOBOBJECT_BASIC_LIMIT_INFORMATION结构,为基本限制的一个超集 ~~~c typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION { JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; IO_COUNTERS IoInfo; SIZE_T ProcessMeomoryLimit; // 调拨给作业中任何一个进程的存储空间峰值 SIZE_T JobMeomoryLimit; // 调拨给作业中全部进程的存储空间峰值 SIZE_T PeakProcessMemoryUsed; // 限制作业中任何一个进程所使用的已调拨存储空间峰值 SIZE_T PeakJobMemoryUsed; // 限制作业中全部进程所使用的已调拨存储空间峰值 } JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION; ~~~ **基本UI限制**:只有一个数据成员,标志位如下表 ~~~c typedef struct _JOBOBJECT_BASIC_UI_RESTRICTIONS { DWORD UIRestrictionClass; } JOBOBJECT_BASIC_UI_RESTRICTIONS, *PJOBOBJECT_BASIC_UI_RESTRICTIONS; ~~~ | 标志 | 描述 | | ----------------------------------- | ------------------------------------------------------------------ | | JOB_OBJECT_UILIMIT_EXITWINDOWS | 阻止进程通过ExitWIndowsEx函数注销、关机、重启或断开系统电源 | | JOB_OBJECT_UILIMIT_READCLIPBOARD | 阻止进程读取剪贴板内容 | | JOB_OBJECT_UILIMIT_WRITECLIPBOARD | 阻止进程清楚剪贴板内容 | | JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | 阻止进程通过SystemParametersInfo函数更改系统参数 | | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | 阻止进程通过ChangeDisplaySettings函数更改显示设置 | | JOB_OBJECT_UILIMIT_GLOBALATOMS | 为作业指定其专有的全局原子表,并限定作业中的进程只能访问此作业的表 | | JOB_OBJECT_UILIMIT_DESKTOP | 阻止进程使用CreateDesktop或SwitchDesktop函数来创建或切换桌面 | | JOB_OBJECT_UILIMIT_HANDLES | 阻止进程中的进程使用作业外部的进程所创建的USER对象(比如HWND) | 要为作业中的进程创建真正安全的沙箱,需要对UI句柄限制。不过有时仍需要让作业内部的进程与作业外部进程通信:可以使用窗口消息,但如果作业中的进程不能访问UI句柄,作业内部的进程就不能向作业外部的进程创建的一个窗口发送或发布窗口消息,但可以使用UserHandleGrantAccess函数来处理: ~~~c BOOL UserHandleGrantAccess(HANDLE hUserObj, HANDLE hJob, BOOL bGrant); ~~~ hUserObj指向一个想允许/拒绝作业内部的进程访问的User对象,一般是窗口句柄,也可能是桌面、挂钩、图标或菜单;后两个参数指出要向哪个作业授予/拒绝访问权限 **安全限制**:一旦应用无法撤销 ~~~c typedef struct _JOBOBJECT_SECURITY_LIMIT_INFORMATION { DWORD SecurityLimitFlags; HANDLE JobTaken; // 由作业中所有进程使用的访问令牌 PTOKEN_GROUPS SidsToDisable; // 指定要禁止对哪些SID进行访问检查 PTOKEN_PRIVILEGES PrivilegesToDelete; // 指定要从访问令牌中删除哪些特权 PTOKEN_GROUPS RestrictedSids; // 指定应添加到访问令牌中的一组仅拒绝的SID } JOBOBJECT_SECURITY_LIMIT_INFORMATION, *PJOBOBJECT_SECURITY_LIMIT_INFORMATION; ~~~ 如何查询上面这些限制,请使用QueryInformationJobObject ~~~c BOOL QueryInformationJobObject( HANDLE hJob, JOBOBJECTINFOCLASS JobObjectInformationCLass, PVOID pvJobObjectInformationSize, PDWORD pdwReturnSize ); ~~~ hJob为NULL可以查看自己的限制 ### 将进程放入作业中 创建子进程后在允许其运行前,调用AssignProcessToJobObject,将进程显式放入新建的作业中 ~~~c BOOL AssignProcessToJobObject(HANDLE hJob, HANDLE hProcess); ~~~ 该函数向系统表明将此进程视为现有作业的一部分,一旦进程已属于某个作业的一部分,就不能再移至另一个作业 ### 终止作业中的所有线程 ~~~c BOOL TerminateJobObject(HANDLE hJob, UINT uExitCode); ~~~ TerminateJobObject为作业内的每个进程调用TerminateProcess,将所有退出代码设置为uExitCode ### 作业通知 * 作业中的所有进程在何时终止执行 * 所有已分配的CPU时间是否已经到期 * 作业内部何时生成了一个新的进程 * 作业中的一个进程何时终止执行 想知道上面的得学作业通知,要获得高级通知,必须在程序中建立更多基础结构:必须创建一个I/O完成端口内核对象,将作业对象与完成端口关联,可以调用SetInformationJobObject将完成端口和一个作业关联起来 ~~~c JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp; joacp.CompletionKey = 1; // 任何唯一标识改作业的值 joacp.CompletionPort = hIOCP; // 用于接收通知的完成端口的句柄 SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(jaocp)); ~~~ 执行上述代码后,系统将监视作业,只要有事件发生,就会把他们投递到I/O完成端口。线程通过调用GetQueuedCompletionStatus来监视完成端口 ~~~c BOOL GetQueuedCompletionStatus(HANDLE hIOCP, PDWORD pNumBytesTransferred, PULONG_PTR pCompletionKey, POVERLAPPED *pOverlapped, DWORD dwMilliseconds); ~~~ 这个函数通过pCompletionKey值说明有哪些作业事件通知,pNumBytesTransferred值指出具体发生了什么事件,pOverlapped根据事件不同数据类型也不同,在作业事件中,它表示的是一个对应的进程ID 作业学的有点云里雾里的 最后修改:2025 年 12 月 03 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏