WDM驱动程序设计 第 5 讲 同步技术.

Slides:



Advertisements
Similar presentations
3 的倍数的特征 的倍数有 : 。 5 的倍数有 : 。 既是 2 的倍数又是 5 的倍数有 : 。 12 , 18 , 20 , 48 , 60 , 72 , , 25 , 60 ,
Advertisements

一、 一阶线性微分方程及其解法 二、 一阶线性微分方程的简单应用 三、 小结及作业 §6.2 一阶线性微分方程.
7.1 内置对象概述及分类 JSP 视频教学课程. JSP2.2 目录 1. 内置对象简介 1. 内置对象简介 2. 内置对象分类 2. 内置对象分类 3. 内置对象按功能区分 3. 内置对象按功能区分 4. 内置对象作用范围 4. 内置对象作用范围.
高级服务器设计和实现 1 —— 基础与进阶 余锋
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Chapter 2 Windows 系統總述 Team 1 (page 17 ~ page 56)
小学生游戏.
Oracle数据库 Oracle 子程序.
§5 微分及其应用 一、微分的概念 实例:正方形金属薄片受热后面积的改变量..
§5 微分及其应用 一、微分的概念 实例:正方形金属薄片受热后面积的改变量..
在PHP和MYSQL中实现完美的中文显示
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
Kvm异步缺页中断 浙江大学计算机体系结构实验室 徐浩.
Hadoop I/O By ShiChaojie.
OpenMP简介和开发教程 广州创龙电子科技有限公司
走进编程 程序的顺序结构(二).
辅导课程六.
网络常用常用命令 课件制作人:谢希仁.
临界区软件互斥软件实现算法.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
8个处理IRP的模型
Windows网络操作系统管理 ——Windows Server 2008 R2.
本节内容 模拟线程切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
逆向工程-汇编语言
临界区软件互斥软件实现算法 主讲教师:夏莹杰
China’s Software Industry August 2006 Instructor: Hengming Zou, Ph.D.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
程序设计工具实习 Software Program Tool
若2002年我国国民生产总值为 亿元,如果 ,那么经过多少年国民生产总值 每年平均增长 是2002年时的2倍? 解:设经过 年国民生产总值为2002年时的2倍, 根据题意有 , 即.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
内容摘要 ■ 课程概述 ■ 教学安排 ■ 什么是操作系统? ■ 为什么学习操作系统? ■ 如何学习操作系统? ■ 操作系统实例
C语言程序设计 主讲教师:陆幼利.
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
第四章 MCS-51定时器/计数器 一、定时器结构 1.定时器结构框图
同步問題
本节内容 随机读取 视频提供:昆山爱达人信息技术有限公司.
实验四、TinyOS执行机制实验 一、实验目的 1、了解tinyos执行机制,实现程序异步处理的方法。
第二章 Java基本语法 讲师:复凡.
微机原理与接口技术 微机原理与接口技术 朱华贵 2015年11月13日.
实验七 安全FTP服务器实验 2019/4/28.
<编程达人入门课程> 本节内容 内存的使用 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
Lightweight Data-flow Analysis for Execution-driven Constraint Solving
Select模型 本节内容 视频提供:昆山爱达人信息技术有限公司 视频录制:yang 官网地址:
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
信号量(Semaphore).
iSIGHT 基本培训 使用 Excel的栅栏问题
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第4课时 绝对值.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
Visual Basic程序设计 第13章 访问数据库
HSC高速输出例程 HORNER APG.
临界区问题的硬件指令解决方案 (Synchronization Hardware)
第15讲 特征值与特征向量的性质 主要内容:特征值与特征向量的性质.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Python 环境搭建 基于Anaconda和VSCode.
本节内容 Windows线程切换_时钟中断切换 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
WSAAsyncSelect 模型 本节内容 视频提供:昆山爱达人信息技术有限公司 视频录制:yang
Google的云计算 分布式锁服务Chubby.
阻塞式模型 本节内容 视频提供:昆山爱达人信息技术有限公司 视频录制:yang 官网地址:
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
上节复习(11.14) 1、方式2、方式0的特点? 2、定时/计数器的编程要点? 3、实验5方案优化问题.
本节内容 进程 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
上节复习(11.7) 1、定时/计数器的基本原理? 2、定时/计数器的结构组成? 3、定时/计数器的控制关系?
创建、启动和关闭Activity 本讲大纲: 1、创建Activity 2、配置Activity 3、启动和关闭Activity
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
本节内容 SEMAPHORE 视频提供:昆山滴水信息技术有限公司 官网地址: 论坛地址: QQ交流 :
§2 自由代数 定义19.7:设X是集合,G是一个T-代数,为X到G的函数,若对每个T-代数A和X到A的函数,都存在唯一的G到A的同态映射,使得=,则称G(更严格的说是(G,))是生成集X上的自由T-代数。X中的元素称为生成元。 A变, 变 变, 也变 对给定的 和A,是唯一的.
Presentation transcript:

WDM驱动程序设计 第 5 讲 同步技术

主要内容 一个同步问题的例子 中断请求级 自旋锁 内核同步对象 其它内核同步原语

一个同步问题的例子 下面利用静态变量lActiveRequests记录当前未完成的 I/O请求数: static LONG lActiveRequests; NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp) { ++lActiveRequests; ... // process PNP request --lActiveRequests; }

有什么问题? mov eax, lActiveRequests add eax, 1 mov lActiveRequests, eax 关于语句“++lActiveRequests”在X86处理器上汇编程序生成如下 代码: // ++lActiveRequests; mov eax, lActiveRequests add eax, 1 mov lActiveRequests, eax 上述代码的第三条指令被执行之前如果被同一CPU上的其它执 行线程打断,或者在不同CPU上有完全相同的代码在同时运行 都会引起++lActiveRequests的计数错误。

解决的办法 把load/add/store和load/subtract/store指令序列替换为 原子指令: // ++lActiveRequests; inc lActiveRequests // --lActiveRequests; dec lActiveRequests INC和DEC指令不能被中断,但是多处理器环境中仍 然是不安全的,因为这两个指令都是由几条微代码实 现的。

最终解决办法 lock inc lActiveRequests // --lActiveRequests; lock dec lActiveRequests LOCK指令前缀可以使当前执行多微码指令的CPU锁 定总线,从而保证数据访问的完整性。

两个最差的假定 驱动程序开发者必须做如下两个最差的假定: 操作系统可以在任何时间抢先任何例程并停留任何长的时间,所以我们不能保证自己的任务不被干扰或延迟。 即使我们能防止被抢先,但其它CPU上执行的代码也会干扰我们代码的执行,甚至一个程序的代码可以在两个不同线程的上下文中并发执行。

同步请求级 一个确定的CPU上的活 动仅能被拥有更高IRQL 的活动抢先。

IRQL与线程优先级 线程优先级是与IRQL非常不同的概念。线程优先级控制着OS线程调度器的调度动作,决定何时抢先运行线程以及下一次运行什么线程。 当IRQL级高于或等于DISPATCH_LEVEL级时线程切换停止,无论当前活动的是什么线程都将保持活动状态直到IRQL降到DISPATCH_LEVEL级之下。 在进行线程调度时会切换线程上下文;按照IRQL进行活动抢先时不会切换线程上下文。

利用IRQL进行同步 KIRQL oldirql; 方法:将所有对共享数据的访问都应该在同一(提升的,高于PASSIVE_LEVEL级的) IRQL上进行。 上述方法只适用于单CPU。 可利用KeRaiseIrql和KeLowerIrql函数改变当前IRQL。 KIRQL oldirql; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeRaiseIrql(DISPATCH_LEVEL, &oldirql); ++lActiveRequests; ... KeLowerIrql(oldirql);

自旋锁(spin lock) 利用自旋锁可以解决多处理器平台上的同步问题。 一个自旋锁对应一个内存变量。 为了获得一个自旋锁,在某CPU上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于它是原子操作,所以在该操作完成之前其它CPU不可能访问这个内存变量。 如果测试结果表明锁已经空闲,则程序获得这个自旋锁并继续执行。 如果测试结果表明锁仍被占用,程序将在一个小的循环内重复这个“测试并设置(test-and-set)”操作,即开始“自旋”。 最后,锁的所有者通过重置该变量释放这个自旋锁,于是,某个等待的test-and-set操作向其调用者报告该自旋锁已释放。

使用自旋锁时的注意事项 第一,如果一个已经拥有某个自旋锁的CPU想第二次获得这个自旋锁,则该CPU将死锁(deadlock)。 第三,仅能在低于或等于DISPATCH_LEVEL级上请求自旋锁,在你拥有自旋锁期间,内核将把你的代码提升到DISPATCH_LEVEL级上运行。

如何使用自旋锁 首先,在非分页内存中为一个KSPIN_LOCK对象分配存储空间。然后调用KeInitializeSpinLock初始化这个对象。 typedef struct _DEVICE_EXTENSION { ... KSPIN_LOCK QLock; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; NTSTATUS AddDevice(...) { PDEVICE_EXTENSION pdx = ...; KeInitializeSpinLock(&pdx->QLock); }

如何使用自旋锁 当代码运行在低于或等于DISPATCH_LEVEL级时获取这个锁,并执行需要保护的代码,最后释放自旋锁。 NTSTATUS DispatchSomething(...) { KIRQL oldirql; PDEVICE_EXTENSION pdx = ...; KeAcquireSpinLock(&pdx->QLock, &oldirql); ... KeReleaseSpinLock(&pdx->QLock, oldirql); }

如何使用自旋锁 如果知道代码已经处在DISPATCH_LEVEL级上 ,如DPC、StartIo,和其它执行在DISPATCH_LEVEL级上的驱动程序例程,可以调用两个专用函数来操作自旋锁 : KeAcquireSpinLockAtDpcLevel(&pdx->QLock); ... KeReleaseSpinLockFromDpcLevel(&pdx->QLock);

内核同步对象 利用内核同步对象可以暂时阻塞一个线程的执行,同步不同线程的执行动作。 内核同步对象仅影响OS线程调度器的调度动作,因此一般只在低于DISPATCH_LEVEL级的代码中用于阻塞线程。 在驱动程序中,只能在“非任意线程上下文”条件下利用内核同步对象阻塞调用者的线程或产生该请求的线程。 在“任意线程上下文”调用等待原语只会阻塞一个“无辜”的线程。

非任意线程上下文 如果驱动程序的回调例程能确切知道处于哪个线程上下文中,则称处于“非任意线程上下文”;大部分时间里,驱动程序无法知道这个事实,即处于“任意线程上下文”中。 非任意线程上下文的例子: 设备的最高级驱动程序的IRP处理函数可以确切地知道它执行在发出该I/O请求的应用程序线程的上下文中。 PNP类IRP的处理函数可以确切地知道它执行在一个系统线程(System Thread)中。 在你自己创建的内核模式系统线程中。(PsCreateSystemThread) DriverEntry、AddDevice、DriverUnload等函数执行在一个系统线程(System Thread)中。

常用的内核同步对象 对象 数据类型 描述 Event(事件) KEVENT 阻塞一个线程直到检测到某事件发生 Semaphore(信号灯) KSEMAPHORE 控制多个线程对共享资源的访问 Mutex(互斥) KMUTEX 执行到关键代码段时,禁止其它线程执行该代码段 Timer(定时器) KTIMER 推迟线程执行一段时期 Thread(线程) KTHREAD 阻塞一个线程直到另一个线程结束

在单同步对象上等待 在任何时刻,任何对象都处于两种状态中的一种:信号态(signaled)或非信号态(not signaled) 。 调用KeWaitForSingleObject或KeWaitForMultipleObjects函数可以使代码(以及背景线程)在一个或多个同步对象上等待,等待它们进入信号态。 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); LARGE_INTEGER timeout; NTSTATUS status = KeWaitForSingleObject(object, WaitReason, WaitMode, Alertable, &timeout);

KeWaitForSingleObject参数含义 KeWaitForSingleObject(object, WaitReason, WaitMode, Alertable, &timeout); object 指向要等待的对象,它应该指向一个上面表中列出的同步对象。该对象必须在非分页内存中。 WaitReason 是一个纯粹建议性的值,KWAIT_REASON枚举型,一般取值为Executive。 WaitMode 是MODE枚举类型,该枚举类型仅有两个值:KernelMode和UserMode。一般取值为KernelMode 。 Alertable 参数一般指定为FALSE。 timeout 是一个64位超时值的地址,单位为100纳秒。正数的超时表示一个从1601年1月1日起的绝对时间。负数代表相对于当前时间的时间间隔。 指定为0将使等待函数立即返回。指定为NULL代表无限期等待。

KeWaitForSingleObject返回值含义 STATUS_SUCCESS,表示等待被满足。即你调用KeWaitForSingleObject时,对象或者已经进入信号态,或者在等待中进入信号态使等待返回。 STATUS_TIMEOUT指出在指定的超时期限内对象未进入信号态 。如果指定0超时,则函数将立即返回。返回代码为STATUS_TIMEOUT,代表对象处于非信号态,返回代码为STATUS_ SUCCESS,代表对象处于信号态。 其它两个返回值STATUS_ALERTED和STATUS_USER_APC表示等待提前终止,对象未进入信号态 。

在多个同步对象上等待 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); LARGE_INTEGER timeout; NTSTATUS status = KeWaitForMultipleObjects(count, objects, WaitType, WaitReason, WaitMode, Alertable, &timeout, waitblocks); objects指向一个指针数组,每个数组元素指向一个同步对象,count表示数组中指针的个数 。 WaitType是枚举类型,其值可以为WaitAll或WaitAny,它指出你是等到所有对象都进入信号态,还是只要有一个对象进入信号态就可以。 waitblocks参数指向一个KWAIT_BLOCK结构数组,内核用它来记录每个对象在等待中的状态。 不需要你对其进行初始化。

KeWaitForMultipleObjects的返回值 如果指定了WaitAll,则返回STATUS_SUCCESS表示等待的所有对象都进入了信号态。 如果指定了WaitAny,则返回值在数值上等于进入信号态的对象在objects数组中的索引。 如果碰巧有多个对象进入了信号态,则返回值仅代表其中的一个,可能是第一个也可能是其它。可以认为返回值等于STATUS_WAIT_0加上数组索引。 NTSTATUS status = KeWaitForMultipleObjects(...); if (NT_SUCCESS(status)) { iSignalled = status - STATUS_WAIT_0; ... }

内核事件(Event)对象 用途:把一个特定的事件通知给一个等待中的线程。 与该对象相关的内核服务函数如下: 服务函数 功能 KeInitializeEvent 初始化事件对象 KeSetEvent 把事件设置为信号态,返回前一个状态 KeResetEvent 把事件设置为非信号态,返回前一个状态 KeClearEvent 把事件设置为非信号态,不报告以前的状态。 KeReadStateEvent 取事件的当前状态。

通知事件与同步事件 通知事件(notification event)有这样的特性,当它进入信号态后,它将一直处于信号态直到明确地把它重置为非信号态。因此,当通知事件进入信号态后,所有在该事件上等待的线程都被释放。 同步事件(synchronization event):只要有一个线程被释放,该事件就被自动重置为非信号态。 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KEVENT event; KeInitializeEvent(event, EventType, initialstate); EventType是一个枚举值,可以为NotificationEvent或SynchronizationEvent。 initialstate是布尔量,为TRUE表示事件的初始状态为信号态,为FALSE表示事件的初始状态为非信号态。

KeSetEvent函数 调用KeSetEvent函数可以把事件置为信号态: event参数指向一个事件对象。 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); LONG wassignalled = KeSetEvent(event, boost, wait); event参数指向一个事件对象。 boost值用于提升等待线程的优先级,使得该线程等待的条件被满足后可以很快获得CPU执行权。 wait参数指定为FALSE。 如果该事件已经处于信号态,则该函数返回非0值。如果该事件处于非信号态,则该函数返回0。

利用事件对象实现互斥操作 typedef struct _DEVICE_EXTENSION { ... ... ... ... ... ... KEVENT lock; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; KeInitializeEvent(&pdx->lock, SynchronizationEvent, TRUE); void thread () { KeWaitForSingleObject(&pdx->lock, Executive, KernelMode, FALSE, NULL); // do something KeSetEvent(&pdx->lock, EVENT_INCREMENT, FALSE); }

在应用层异步访问设备 // CreateFile的一个参数可以规定同步方式还是异步方式访问该设备 hDevice = CreateFile(“\\\\.\\wdm1Device”, ……….); HANDLE waitEvent = CreateEvent(……….); OVERLAPPED ol; ……… ol.hEvent = waitEvent; ReadFile( hDevice, buffer, NumberOfBytesToRead, &ol); while(WaitForSingleObject(waitEvent, 100)==WAIT_TIMEOUT) { if(!KeepRunning) { CancelIo(hDevice); goto EXIT; } // 从buffer中访问数据……

内核信号灯 内核信号灯是一个有同步语义的整数计数器。 信号灯计数器为正值时代表信号态,为0时代表非信号态。计数器不能为负值。 释放信号灯将使信号灯计数器增1,在一个信号灯上等待将使该信号灯计数器减1。如果计数器值被减为0,则信号灯进入非信号态,之后其它调用KeWaitXxx函数的线程将被阻塞。 注意如果等待线程的个数超过了计数器的值,那么并不是所有等待的线程都可以恢复运行。

服务函数与使用方法 KeInitializeSemaphore:初始化信号灯对象 KeReadStateSemaphore:取信号灯当前状态 KeReleaseSemaphore:释放信号灯对象 KSEMAPHORE semaphore; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); KeInitializeSemaphore(&semaphore, count, limit); …. KeWaitForSingleObject(&semaphore, …..); …….. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeReleaseSemaphore(semaphore, boost, delta, wait);

互斥对象Mutex 互斥(mutex)就是mutual exclusion的简写。 内核互斥对象为多个竞争线程串行化访问共享资源提供了一种方法。虽然用其它方法也能实现此功能,但互斥对象加入了一些措施能防止死锁。 如果互斥对象不被某线程所拥有,则它是信号态,反之则是非信号态。 如果需要长时间串行化访问一个对象,应该首先考虑使用互斥(而不是依赖提升的IRQL和自旋锁)。 利用互斥对象控制资源的访问,可以使其它线程分布到多处理器平台上的其它CPU中运行,还允许导致页故障的代码仍能锁定资源而不被其它线程访问。

互斥对象的服务函数 KeInitializeMutex 初始化互斥对象 KeReadStateMutex 取互斥对象的当前状态 KeReleaseMutex 设置互斥对象为信号态 KMUTEX mutex; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); KeInitializeMutex(&mutex, level); …. KeWaitForSingleObject(&mutex, …..); …….. KeReleaseMutex(&mutex, wait);

内核定时器 (Timer) Timer对象可以在指定的绝对时间或间隔时间后自动从非信号态变为信号态。它还可以周期性的进入信号态。 可以利用KeWaitXxxx函数等待一个Timer对象在某个时间间隔后进入信号态,也可以利用Timer对象安排一个在某个时间间隔后或定期执行的DPC回调函数。 定时器也分为通知型和同步型两种。通知型定时器及时结束后一直处于信号态,除非手动改变。因此,所有等待它的线程都被释放。同步定时器正相反,它只允许有一个等待线程。一旦有线程在这种定时器上等待,并且开始执行,定时器就自动进入非信号态。

内核定时器的服务函数 服务函数 功能 KeInitializeTimer 初始化一次性的通知型定时器 KeInitializeTimerEx 初始化一次性的或周期性的通知型的或同步型定时器 KeSetTimer 为通知型定时器设定时间或DPC对象 KeSetTimerEx 为定时器设定时间、周期和其它属性 KeCancelTimer 取消一个定时器 KeReadStateTimer 获取定时器的当前状态。

一次性定时器的用法 KTIMER timer; // someone gives you this ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeInitializeTimerEx(&timer, NotificationTimer); // KeInitializeTimer(timer); LARGE_INTEGER duetime; KeSetTimer(&timer, duetime, NULL); KeWaitForSingleObject(&timer, ......); .....

周期性定时器的用法 KTIMER timer; // someone gives you this ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KeInitializeTimerEx(&timer, SynchronizationTimer); LARGE_INTEGER duetime; long period; KeSetTimerEx(&timer, duetime, period, NULL); while(True) { KeWaitForSingleObject(&timer, ......); ..... } KeCancelTimer(&timer);

定时器与DPC PKDPC dpc; // points to KDPC you've allocated ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); KeInitializeTimer(timer); KeInitializeDpc(dpc, DpcRoutine, context); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); LARGE_INTEGER duetime; KeSetTimer(timer, duetime, dpc); ...... ..... .......... ........ VOID DpcRoutine(PKDPC dpc, PVOID context, .....) { ... }

定时函数 KeDelayExecutionThread :可以在PASSIVE_LEVEL级上调用该函数并给出一个时间间隔。该函数省去了使用定时器时的麻烦操作,如创建,初始化,设置等待操作。 如果需要延迟一段非常短的时间(少于50毫秒),可以在任何IRQL级上调用KeStallExecutionProcessor。这个延迟的目的是允许硬件在程序继续执行前有时间为下一次操作做准备。实际的延迟时间可能大大超过请求的时间。 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); LARGE_INTEGER duetime; NSTATUS status = KeDelayExecutionThread(WaitMode, Alertable, &duetime);

内核线程对象 内核线程对象(PKTHREAD)代表一个内核线程,可以利用KeWaitXxx等待原语在一个内核线程上进行等待,等待者会被一直阻塞直到所等待的内核线程执行完毕。 HANDLE hthread; PKTHREAD thread; PsCreateSystemThread(&hthread, ...); ObReferenceObjectByHandle(hthread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*) &thread, NULL); ZwClose(hthread); KeWaitForSingleObject(&thread, …..);

快速互斥对象(fast mutex) FAST_MUTEX fastmutex; 快速互斥对象通过对无竞争情况的优化处理,可以提供比普通内核互斥对象更快的执行性能。 获取一个快速互斥对象后其拥有者线程一般会被提升到APC_LEVEL级,所以其拥有者在使用某些内核服务函数时会受到限制。 FAST_MUTEX fastmutex; ExInitializeFastMutex(FastMutex); ExAcquireFastMutex(FastMutex); … … … … … … ExReleaseFastMutex(FastMutex);