NT内核和驱动开发的基础知识-笔记

这是我在学习NT内核和驱动开发的基础知识时记录的一些笔记,不是连续的教程,欢迎指正错误的地方
--------------------------------------------------------------------------------------------

NT内核模式组成部分(从上到下:NT执行体/NT内核/HAL硬件抽象层):

1、HAL硬件抽象层
   NT内核看到的是HAL提供的理想化的硬件视图,所有不同硬件结构的差异由HAL在内部处理,OS的核心组件和安装到操作系统的设备驱动程序可以调用HAL导出的函数。
2、NT内核
   NT内核提供OS和其他部件要使用的基本功能,NT内核负责处理进程和线程的调度,使用SPINLOCK来处理多处理器同步、中断处理和调度以及其他功能。NT内核提供的基本功能:支持内核对象/线程调度/多处理器同步/硬件异常处理/中断处理和调度/陷入处理/硬件特定的其他功能。NT内核不依赖对象管理器来管理NT内核定义的对象类型,NT执行体使用NT内核输出的对象来构造更复杂的对象供用户使用。
NT内核对象有:
  调度器对象:控制系统线程的同步和调度。调度器对象包括:线程/事件/定时器/互斥体/信号量
  控制对象:这些对象影响内核模式代码的运行但不影响调度和同步。包括APC/DPC/中断/进程/设备队列对象。
NT内核还维护下列的数据结构:
  中断调度表:内核维护的和中断源相关的适当的中断服务例程的表格。
  处理器控制块:系统中的每个处理器都有一个处理器控制块(PRCB),包括各种处理器特有的信息,包括当前线程的执行指针,下一个要被调度的线程和空闲线程。
  处理器控制域:这是硬件结构相关的内核数据结构,包含PRCB指针/全局描述符表(GDT)/中断描述符表(IDT)和其他的信息。
  DPC队列:这个全局队列包含每当处理器的IRQL低于DISPATCH_LEVEL的时候将要调用的过程列表。
  定时器队列:NT内核维护一个全局定时器队列,包含在将来某个预定时间要触发的定时器。
  调度器数据库:线程调度器模块维护一个包含所有处理器执行状态和系统所有线程的数据库,调度器模块用于调度线程执行。
除了上面提到的对象类型,NT内核还要维护设备队列/电源通知队列/处理器请求队列和其他的内核为了自己的功能而必须维护的数据结构。
3、NT执行体
   NT执行体的主要组成部分有对象管理器/VMM管理器/进程管理器/I/O管理器/安全引用监视器/本地过程调用设备/配置管理器和缓存管理器。文件系统/设备驱动/中间层驱动是I/O管理器管理的I/O子系统的一部分,它也是NT执行体的一部分。


NT定义和使用中断请求级(IRQLS)来决定内核组件的执行顺序。在内核运行的代码有特定的IRQL,决定了它的硬件优先权。高IRQL可以中断低IRQL。
PASSIVE_LEVEL  普通线程执行的中断请求级
APC_LEVEL      异步过程调用(APC)中断请求级。APC被软件中断调用,将会影响目标线程的控制流。APC的目标线程将被中断,
               然后创建APC时通过参数指定的例程会在目标线程的上下文中执行,中断请求级别是APC_LEVEL级。
DISPATCH_LEVEL 线程调度和延迟过程调用(DPC)中断等级。DPC在多处理器上可能同时运行,每个处理器一个DPC例程。
               DPC运行时不能调用服务例程也不能发生页中断。


APC是线程相关的,APC总是运行在特定的线程上下文中
APC运行在OS预定时刻
APC可以被抢占也可以导致当前运行线程的抢占


NT平台的线程有用户栈(供R0层使用)和内核栈(供R3层使用),当线程请求系统服务从R3切换到R0时,陷阱机制会切换到内核栈,用分配给线程的内核空间的栈来覆盖用户空间的栈。内核栈是固定大小的因此是有限资源,驱动过度使用肯定会不足。NT的高层驱动表现出很多递归行为,特别是FSD、VMM和NT缓存管理器,这可能导致内核栈很快消耗完。
在NT中线程用线程结构体来表示,线程结构体有以下几个部分组成:
用户栈指针和内核栈指针/程序计数器/处理器状态/整型和浮点型寄存器/架构相关的寄存器
句柄是进程相关的,属于进程的线程打开的句柄对该进程的其他线程来说是可以访问的。


线程结构体中有个IrpList域,类型是链表结构体的指针,用作未决IRP链表的头指针,Irp->ThreadListEntry域用于把未决IRP插入该链表。IO子系统试图取消未决IRP时可以遍历该链表。注意IoAllocateIrp例程不会把分配的IRP插入该链表。
线程结构体中有2个APC队列:R0APC队列和R3APC队列,每个队列的APC对象又有特殊和普通属性。特殊APC对象总是排在普通APC对象前面,确保特殊APC对象先运行。
对于R0层来说,特殊APC对象是常用的(通过线程结构体中的KernelRoutine域完成APC任务)
对于R3层来说,普通APC对象是常用的(通过线程结构体中的NormalRoutine域完成APC任务)
每当OS需要丢弃APC队列的内容时候调用线程结构体的RundownRoutine域,APC对象可以在该域中做些析构动作
对于FS来说,管理APC投递是确保FS正确行为的本质(不正确APC投递可能导致额外的IO被触发到FS系统或者FS的重入)。
禁止APC投递的方法:
1、用KeEnterCriticalRegion/KeLeaveCriticalRegion来禁止/允许R0普通APC的投递,但是R0特殊APC还是可以投递的。
   * R0特殊APC不会重入FS因此是安全的。
2、提高IRQL到APC_LEVEL将禁止所有APC的投递(R0普通和R0特殊APC都被禁止)。
NT中的某些同步原语通过提高IRQL到APC_LEVEL禁止APC投递,以便禁止代码重入。ExAcquireFastMutex(进入APC_LEVEL级)/ReleaseFastMutex(退出APC_LEVEL级),因此当拥有FastMutex时该线程的所有APC都不能投递。


线程上下文和陷阱
陷阱是处理器提供的在某些事件发生时捕获执行线程上下文的一种机制。导致陷阱的事件有中断/异常/导致处理器模式从R3转换到R0的系统服务调用。当陷阱发生时,将调用OS的陷阱处理器(陷阱处理器是一段高度依赖处理器和特定架构的汇编代码,也是NT内核提供的核心功能的一部分)。在调用适当的例程处理陷阱情况之前陷阱处理器要把执行线程的信息保存到一个叫做调用帧的表里。调用帧有2个组成部分:陷阱帧(包含易失性寄存器状态) 异常帧(发生异常情况而调用陷阱处理器的时候也要保存非易失性寄存器状态)


在NT中进程用进程结构体来表示,它有一个执行上下文对这个进程来说是唯一的。进程的执行上下文包括进程的虚拟地址空间、进程可见的资源、属于进程的线程。NT内核使用进程环境块结构(PEB)描述一个进程,这个结构对OS的其他部分是不透明的。创建进程时,进程被赋予一个访问令牌,和进程关联的线程访问任何Windows NT对象时用这个令牌来验证它们自己。


对象管理器提供以下功能:
a. 动态添加新对象类型到系统中(注意对象管理器并不关心这个对象的内部数据结构)
   某些对象类型是NT对象管理器预先定义的。注册一个新对象类型时,NT组件提供和新对象类型的实例相关的回调例程,通过NT对象管理器提供的API向NT对象管理器提出定义新对象类型。NT对象管理器记住这些回调例程的指针,当这种新对象类型的实例执行操作(当然这些操作都是由NT管理器定义的通用行为,比如分析、关闭、清除等)时调用这些回调例程。
b. 允许模块指定对象实例的安全和保护特性
c. 提供创建和删除对象实例的方法
d. 允许模块定义对象类型,提供自己的方法(比如创建、关闭、删除操作)来管理对象类型的实例
e. 提供一致的方法逻辑来维护对象类型实例的引用
f. 提供基于更通用的文件系统的层次化的倒置树格式的全局命名层次


对象管理器维护一个全局命名空间,模仿了通常的文件命名习惯,有根目录。R3进程或者R0试图打开一个对象时必须向NT对象管理器提供对象的绝对路径,NT对象管理器会分析这个路径,当NT对象管理器遇到某个对象有相关的分析回调例程时会挂起NT对象管理器的分析而调用对象提供的分析例程来分析路径的剩余部分。
被对象管理器管理的对象类型可以提供分析方法供对象管理器回调。
每个对象实例由标准对象头以及和对象类型相关的特有的对象体2部分组成。
标准对象头包含如对象名字的指针、对象相关的安全描述符、对象的访问模式、引用计数、对象类型的指针(指向拥有的对象实例)和其他相关属性。当线程打开一个特定对象类型的实例,对象管理器返回一个对象实例的不透明句柄给请求线程。注意,任何对象实例可以有不止一个句柄。对象管理器维护和每个对象句柄关联的信息。注意:在句柄和打开的对象类型的实例之间没有直接联系,句柄通常是一个对象数组的索引。警告:句柄是特定于进程的。


NT执行体的其他组件
进程管理器
本地过程调用功能(LPC):一种可以在同一节点(系统)上的进程之间传递消息的机制。Client端进程通过传递参数到Server端进程来请求服务。作为回报,Client端可以收到从Server端返回的处理后的数据。当发生Client端到Server端的调用时候,调用被Client端进程中的一个存根截取,把要传递的参数打包以后通过LPC给Client端进程提供的机制来传输数据到Server端,最后等待Server端回应。这是使用一种LPC定义和创建的叫端口的对象来完成的。LPC模仿RPC的机制,RPC用于实现Client-Server模式通过本地或者广域范围相连的机器。LPC做了更好的优化,因为在同一节点中所有进程访问相同的物理内存。
安全引用监控:负责在本地节点上强制执行安全策略,还提供对象审计功能。
虚拟内存管理:NT虚拟内存管理器(VMM)管理节点上所有的可用物理内存,还负责向OS的其他组件以及节点上执行的所有应用程序提供虚拟内存管理。
缓存管理器:NT执行体包含一个专门的缓存模块来为存储在辅助存储介质的文件数据提供缓存功能(通过使用系统内存)。缓存管理器使用NT执行体的VMM的服务来提供缓存功能。所有的本地FSD都使用缓存管理器。
I/O管理器:定义和管理包含所有内核驱动程序(FSD、网络、磁盘、中间层、过滤驱动程序)的框架


FSD是存储管理子系统的一个组件,为用户提供在持久性介质上存储和读取信息的功能。
FSD通常为用户提供以下功能:
a. 创建修改删除文件(文件是存储在辅助存储设备上的用户数据的总的名字)
b. 安全可控的在用户间传输和共享信息
c. 以适当的方式向应用程序提供结构化的文件内容
d. 用文件的逻辑名字而不是设备特定的名字来表示存储的文件
e. 提供文件的逻辑视图而不是设备相关的视图
以上是基本功能,远程FS(网络和分布式FS)还提供以下功能:
a. 网络透明 b. 位置透明 c. 位置无关 d. 用户可移动 e. 文件可移动


磁盘(本地)FS
本地FS管理的是存储在直接连接到计算机的磁盘上的数据。
Client Thread -> IO SubSystem Manager -> File System Driver -> Internediate and Disk Driver -> Local Volume
客户端线程    -> IO子系统管理器       -> 文件系统驱动(FSD)  -> 中间层和磁盘驱动             -> 本地的物理卷


逻辑卷管理软件通常提供如软件数据镜像、跨越多个物理磁盘、动态调整逻辑卷的大小,所以这些软件常常叫做容错软件。
逻辑卷管理器把磁盘映射为连续的空间呈现给FS,尽管这个空间可能是磁盘的一部分、全部、或者跨越多个磁盘。
每个网络FS由2部分组成:
客户端重定向器:执行在Client端,把Client端对Server端共享目录的访问请求转化为网络请求发送给Server端,并接收Server端的返回结果。
共享节点上的服务器程序:有2个功能:1 定义和Client端交互的协议 2 把Client端送来的网络请求转化为对本地的FS的请求,把本地FS请求的结果通过网络返回给Client端。
多提供者路由器(MPR)和多通用命名规范提供者(MUP)模块和网络重定向器交互,向Client端提供本地FS的外观。这些组件和内核模式的网络重定向器一起,负责把远程(共享)逻辑卷FS和Client端的本地名字空间结合起来,因此如果为Windows NT OS设计和开发网络重定向器模块,需要对这些组件有详细了解。
多提供者路由器(MPR)是执行在R3的DLL,作为通用应用程序组件(即网络组件)和可能执行在Client节点上的多网络提供者之间的缓冲。
注意:网络提供者是设计来协助网络重定向器工作的软件模块。网络提供者为系统提供接口,允许网络组件应用程序向网络重定向器以标准风格请求通用功能,而不用为Client端的每种网络重定向器开发不同的代码。
MPR DLL提供网络无关的接口供Win32应用开发人员向网络提供者/网络重定向器请求服务。同时定义一组接口供网络重定向器实现,以便用标准风格和网络重定向器交互。
例如:假设WIN32应用程序要创建一个新的网络连接。1、Win32应用程序调用API WnetAddConnection(),这个API由MPR DLL实现,在API内部会调用NPAddConnection,这个API由MPR DLL定义,网络提供者实现这个API,因此MPR会按照Client的注册表中的顺序(可能就是注册的先后顺序)依次调用注册过的网络提供者实现的这个API,从而使网络提供者收到这个请求,网络提供者通过返回结果告诉MPR是否处理这个请求。


对NT用户来说,最常见的例子是LAN Manager Network,支持共享目录、本地卷、打印机和其他资源。由在Client端执行的LAN管理器重定向器组件和在Server端执行的LAN管理器服务器组件组成。LAN管理器服务器组件导出本地FS或其他资源如打印机,这两个组件使用SMB(Server Message Block)协议通信。
分布式FS从标准网络FS发展而来,是用户使用单一名字空间而完全隐藏数据所在的物理位置的FS。
特殊FS:提供类似FS的接口但是调用这些接口时却做和FS完全不同的事情。例如:提供分级存储管理(HSM)功能的驱动或者提供虚拟FS的驱动(比如一些商用源代码管理系统)
FS是IO子系统的组成部分,因此遵循NT I/O管理器定义的接口。NT I/O管理器定义了所有内核模式驱动程序必须遵循的标准接口。这个接口适用于本地FSD、网络和分布式FS重定向器软件、中间层驱动、过滤驱动、设备驱动。
用户有2种方法使用FS提供的服务:
1、使用标准API,通过OS向FSD发请求,内部是通过IRP/FastIO实现。
   通常流程是:打开/创建->读/写->关闭
   打开/创建:通过OS子系统(例如WIN32子系统)API发起打开/创建请求->调用NtXXX例程->
             I/O管理器调用对象管理器分析用户提供的名字->I/O管理器定位管理已经挂载的逻辑卷的FSD->
             I/O管理器调用FSD的打开/创建例程处理用户请求->FSD处理完成后逆序返回给用户
   读/写:打开/创建完成后用户层会得到一个句柄,对应着内核中由I/O管理器管理的文件对象(FileObject),
          用户通过句柄发起读/写请求,内核通过句柄对应的文件对象完成读写请求,把请求的结果返回给用户。
   关闭: 用户通过句柄发起关闭文件请求,内核通过句柄对应的文件对象完成关闭请求(释放对应的资源),把请求的结果返回给用户。
2、通过文件系统控制接口(FSCTL)直接向FSD发请求。


如果在分配的内存里需要使用同步结构必须使用非分页内存,这就是为什么驱动开发中带锁的数据结构都定义为全局变量的原因,全局变量存在于非分页内存。使用ExInitialize(N)PagedLookasideList/ExAllocateFrom(N)PagedLookasideList/ExFreeTo(N)PagedLookasideList使用池内存,如果架构支持原子8位比较交换指令,lookaside将不使用自旋锁来执行同步,这可以提高性能。注意:池内存的头必须从非分页内存中分配,一般驱动程序把这个头定义为全局变量。


异常将导致异常信息在线程上下文中同步处理,因为异常情况是在指令执行时直接产生的同步事件。异常调度支持和基于调用帧的异常处理注册是NTOS提供的而不是编译器提供的功能,换句话说,除非使用支持NT异常处理模式的编译器,否则就不能使用OS支持的异常处理的特性。FSD开发中使用SEH的原因是:1 稳定性 2 NT缓存管理器支持例程和有些VMM例程通过触发异常代替返回错误。SEH要求编译器和OS都支持。在内核中,SEH能处理的异常是有限的,换句话说,使用了SEH仍然可能KeBugCheck


在try-finally结构的try部分使用return可能引起编译器执行调用帧展开,这是昂贵的操作。在想使用return的地方用goto跳到try的末尾可以避免这种昂贵的展开操作,可见goto不是一无是处。
同步原语 代码执行进入临界区称为同步原语无信号 代码执行离开临界区称为同步原语有信号 等待操作是在等待同步原语有信号
自旋锁SPINLOCK:用于多处理器环境下线程同步。
NT平台有2种自旋锁:1 中断自旋锁 2 执行自旋锁
中断自旋锁用来同步例程的执行和中断例程ISR的执行。
执行自旋锁只能被IRQL<=DISPATCH_LEVEL的线程获得,不能用执行自旋锁来执行和ISR的同步。
自旋锁必须存储在非分页内存
多个线程竞争获取自旋锁的规则是谁的IRQL最高。当运行在某个处理器上的某个线程得到自旋锁,将会禁止线程切换(即线程不能被抢占),但是仍然可能被更高的IRQL中断。内核实现自旋锁的实际方法依赖于处理器,但是通常使用原子测试-原子设置汇编指令来实现自旋锁。为了减少总线的争夺,OS使用一次原子测试-原子设置指令,如果发现锁状态被设置为忙,就用平常的检测指令检测锁状态直到锁状态变为空闲,这时再用原子测试-原子设置指令去试着得到锁,如果恰好此时锁状态又变为忙,OS就重复执行原子测试-原子设置指令。
保证自旋锁正确行为的规则:
1、在获取SPINLOCK和释放SPINLOCK之间的代码不能操作非分页内存,因为IRQL等级是DISPATCH_LEVEL。
2、SPINLOCK在节点上所有处理器上共享,所以尽可能少占用SPINLOCK(最晚获取最早释放),避免OS效率低下。
3、不要嵌套使用同步原语,可能引起OS死锁。


设备驱动的调度例程通常执行在任意线程上下文中,所以不能等待调度器对象。在高于PASSIVE_LEVEL的IRQL执行时,在非0时间间隔等待一个调度器对象被认为是一个致命错误,因此大多数设备驱动开发者不能使用调度器对象来同步,但是调用层次局限于文件系统的FSD或FSD过滤驱动开发者可以潜在的使用调度器对象。
FSD的调度例程通常执行在特定线程上下文中(System线程上下文或从R3发出IO请求的线程上下文),所以可以等待调度器对象被设置为有信号状态。
NT内核提供的调度器对象必须被作为透明的数据结构,NT内核提供初始化/查询状态/设置状态/清除状态的例程,使用者必须在非分页内存中为调度器对象提供存储空间。
事件对象:用于多个线程之间同步,它记录事件的发生来决定执行流程。
有两种类型的事件对象:
通知事件对象 有信号时所有等待线程被满足,不会自动变为无信号
同步事件对象 有信号时只有一个等待线程被满足,会自动变为无信号
多个驱动通过使用有名字的事件对象来同步访问共享数据,使用IoCreateSynchronizationEvent创建或者打开一个有名字的事件对象,如果是创建自动设置为有信号状态。
KeInitializeEvent初始化事件对象
KeSetEvent设置事件对象的状态为有信号
KeResetEvent/KeClearEvent设置事件对象的状态为无信号,KeResetEvent还返回事件对象原来的状态
KeReadStateEvent得到事件对象的状态
KeWaitForSingleObject


有两种类型的定时器对象:
通知型定时器 有信号时所有等待线程被满足,不会自动变为无信号
同步型定时器 有信号时只有一个等待线程被满足,会自动变为无信号
KeInitializeTimer(Ex)在非分页池中分配定时器对象指针,初始化为无信号
KeSetTimer(Ex)设置定时器对象,可以指定定时器有信号时执行的DPC例程
KeReadStateTimer得到定时器的当前状态
KeCancelTimer取消先前设定的定时器,如果定时器有相联系的DPC例程也会被取消


互斥体对象
和SPINLOCK类似,区别是无信号时互斥体对象会睡眠而SPINLOCK会自旋。存储互斥体对象的空间必须从非分页内存中分配,得到互斥体对象和释放互斥体对象之间的代码不能引起页故障。
有两种类型的互斥体对象
快速互斥体对象 是事件调度器对象的包装,只是同步类型的事件对象的别名。快速互斥体对象不提供死锁防止支持,也不能递归的请求。快速互斥体对象是由NT执行体支持的,因为快速互斥体对象不属于NT内核导出的原始的同步机制。
ExInitializeFastMutex()初始化快速互斥体对象,实际上是初始化同步类型的事件对象的宏
ExAcquireFastMutex(Unsafe) 试图得到快速互斥体,如果得不到快速互斥体,请求的线程就阻塞自己直到快速互斥体可用。两个例程的区别是是否关闭分发APC例程给得到快速互斥体的线程。显然,Unsafe不关闭分发APC例程,而是假定得到快速互斥体的线程自身是APC安全的,即可能已经使用了某种手段防止APC调用引起的重入,比如调用了KeEnterCriticalRegion或者已经提高IRQL到APC_LEVEL。
ExReleaseFastMutex(Unsafe) 释放先前得到的快速互斥体,Unsafe例程对应取得时的Unsafe例程
ExTryToAcquireFastMutex 试图得到快速互斥体,成功返回TRUE(将阻塞内核模式的APC),失败返回FALSE
互斥体对象 NT内核提供的,驱动可以在初始化每个互斥体时关联一个等级,从设计来说,等级越低的互斥体对象越先得到,内核会做这个检查,确保先前得到的互斥体对象的等级比当前互斥体的等级低(除非是递归得到同一个互斥体),互斥体可以递归获取,唯一的限制是获取和释放次数同样多,这样NT内核可以正确释放资源。得到互斥体的线程所在的进程不能被发生页故障。
KeInitializeMutex 初始化互斥体对象,驱动必须指定一个有效的非0等级的参数
KeReadStateMutex 得到互斥体对象当前的状态
KeReleaseMutex 释放先前得到的互斥体对象,如果释放互斥体对象的线程希望立即执行内核等待例程(比如KeWaitForSingleObject),应该指定等待参数为TRUE,避免不必要的上下文切换。


信号量对象 允许多个线程同时访问共享数据资源,如果指定只允许一个线程访问共享数据资源,就和互斥体类似。通过指定访问共享数据资源的线程数量控制并行访问。信号量对象可以看成一道门,门开时同时访问共享数据资源是允许的,门关闭时没有线程可以访问共享数据资源。
注意:虽然和互斥体类似,但是信号量对象不提供互斥体提供的死锁检测功能,得到信号量对象也不会引起内核APC关闭,信号量对象的存储空间必须从非分页内存分配。信号量对象的工作过程:每个信号量对象有个引用计数值,0表示有信号,非0表示无信号,当线程获得信号量对象时,引用计数值减1,当线程释放信号量对象时,引用计数值加1,就是说引用计数值决定了有几个线程可以得到信号量(得到信号量的线程会并发运行),所以引用计数值决定了几个线程可以并发运行。
KeInitializeSemaphore初始化信号量对象,可以指定引用计数值,如果指定的引用计数值为非0,信号量对象被设置为有信号状态,即和指定的引用计数值同样数目的等待线程可以得到信号量开始并发运行了,必须指定引用计数值允许的最大值(允许并发访问共享数据资源的线程数)
KeReleaseSemaphore 释放信号量对象,可以指定和信号量相关的引用计数值增加的数值,这可以是一个或多个等待该信号量的线程结束等待。注意:不能指定这个增加的值加上原来的值超过初始化信号量时候指定的上限值,否则会引发一个异常。
KeReadStateSemaphore 得到信号量相关的引用计数的当前值


ERESOURCE对象 读/写锁
WINDOWS NT提供的用于FSD的同步机制,ERESOURCE是提供互斥写共享读语义的结构体对象。读写锁的存储空间必须从非分页内存分配。ERESOURCE结构对资源来说有所属线程的概念,多个读线程可以并发拥有共享资源,读写锁可以递归获取,唯一的限制是获取和释放次数同样多。注意:读写锁结构必须在释放内存前(初始化时候驱动程序为这个结构体分配的内存)反初始化或者从资源结构的全局链表中删除。所有操作读写锁的例程的IRQL<=DISPATCH_LEVEL。ERESOURCE结构使用执行自旋锁来保护在资源内部的域。当得到这个自旋锁时,NT执行体会把IRQL升高到DISPATCH_LEVEL,因此,调用任何高于DISPATCH_LEVEL级的例程将导致死锁。
ExInitializeResourceLite - 初始化驱动程序分配的资源结构(为结构体分配了一块非分页内存)。资源被插入资源结构的全局链表中,因此,在释放分配的非分页内存前反初始化是很重要的。
ExDeleteResourceLite - 把资源从全局链表中脱链,为资源分配的非分页内存随后就可以释放了。
ExAcquireResourceExclusiveLite - 试图为互斥写访问取得资源结构,请求互斥写访问的线程可以指定是否希望阻塞自己直到资源可用。如果线程不希望阻塞并且其他线程已经得到对这个资源的互斥写或共享读,该例程返回FALSE,代表请求失败。
ExTryToAcquire
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值