I/O管理器为每个IRP创建I/O栈数组,其中数组元素对应于分层驱动程序链中的每个驱动程序。每个驱动程序都拥有包中的一个栈位置,并调用IoGetCurrentIrpStackLocation 以获得关于I/O操作的驱动程序特定信息。
这样的链中的每个驱动程序负责调用IoGetNextIrpStackLocation ,然后设置下一个驱动程序的I/O堆栈位置。任何高级驱动程序的I/O堆栈位置也可以用于存储关于操作的上下文,以便驱动程序的IoCompletion 例程可以执行其清理操作。
分层驱动程序中的处理IRP图显示了原始IRP中的两个I/O堆栈位置,因为它显示了两个驱动程序,一个文件系统驱动程序和一个大容量存储设备驱动程序。分层驱动程序中的处理IRP中的驱动程序分配IRP图没有创建它们的FSD的堆栈位置。为低级驱动程序分配IRP的任何高级驱动程序还根据下一级驱动程序的设备对象的StackSize值确定新IRP应该具有多少I/O堆栈位置。
下图更详细地显示了IRP的内容。

IRP处理流程总结
IRP被IO管理器所创建
-
一个IRP被发出来,可以被多个驱动对象所处理,每个驱动对象都能做出不同的处理.
IO管理器创建IRP时,会找出所有能够处理此IRP的设备对象,并为每一个设备对象建立一个IRP栈元素,(一个驱动对象所创建的设备可以挂载到另一个驱动的设备对象链中,IoAttatchDeviceToDeviceStack )
于是便有了IRP栈,
通过函数IoGetCurrentIrpStackLocation 能够获取到本驱动的IRP栈,
-
一个IRP被处理完成之后,使用IoCompleteRequest 来设置IRP的完成状态,设置时,主要设置以下内容
Irp.IoStatus.Status - 将完成的状态设置到此字段(成功了失败了,总之得有一个状态码),这个状态码可以使用STATUS_XXXX 这系列的宏.
Irp.IoStatus.Information - 设置完成的字节数(如读取了多少字节,写入了多少字节等等)
-
IRQL
IRQL即 :中断请求级别(Interrupt ReQuest Level,IRQL) .
内核实际就是一个进程(ntoskrnl.exe ),里面有非常多的全局变量,而且有大量的线程在运行. 当几个线程同时操作一个全局变量时,就会出现问题,为了解决这个问题,微软这才提出了IRQL 的概念. 这套东西主要是为了保证代码执行的优先级,原子级的,它分为以下级别
Dispatch :所有运行在Dispatch级的代码都是会被进行原子操作的,且不能访问分页内存,也就是说操作系统中在一个时间内只能运行一段Dispatch级的代码,且必须将其完全执行完毕后才会发生线程切换
APC :比Dispatch 低的一个级别,可以访问分页内存
Passive : 的优先级,大多数代码所运行的级别
MDL
typedef struct _MDL { ?? struct _MDL *Next; //用于挂入到一个队列中,如插入到驱动程序的IRP的MDL队列中。 ?? CSHORT Size;//指定这个MDL所占的空间大小=MDL结构体的大小+sizeof(PFN_NUMBER)*映射需要的页面数。 ?? CSHORT MdlFlags; //指明MDL的映射方式 ?? struct _EPROCESS *Process; //指明此MDL属于哪个进程。 ?? PVOID MappedSystemVa; //所描述的内存如果有映射到系统空间并锁定。那么这个成员指定了MDL在系统空间内的地址 ?? PVOID StartVa; //所描述的内存映射后的虚拟地址的开始页面地址,这个地址总是页面对齐的地址 ?? ULONG ByteCount; //此MDL所描述的内存块有多少个字节 ?? ULONG ByteOffset; //MDL映射的虚拟地址的首地址在StartVa页面中的偏移值。 } MDL,*PMDL;
一个连续的虚拟内存地址范围可能是由多个分布(spread over)在不相邻的物理页所组成的。系统使用MDL(内存描述符表)结构体来表明虚拟内存缓冲区的物理页面布局。我们应该避免直接访问MDL。我们可以使用MS-Windows提供的宏,他们提供了对这个结构体基本 的访问。
MmGetMdlVirtualAddress 获取缓冲区的虚拟内存地址 ·
MmGetMdlByteCount 获取缓冲区的大小(字节数)
MmGetMdlByteOffset 获取缓冲区开端的物理页的大小(字节数)
MmGetMdlPfnArray 获取记录物理页码的一个数组指针。
我们可以用IoAllocateMdl 函数来分配一个MDL。如果要取消分配,可是使用IoFreeMdl 函数。或者,可以使用MmInitializeMdl 来把一个之前定义的缓冲区定制成一个MDL。但是以上两种方式都不能初始化物理页码数组。
对于在非分页池中分配的缓冲区,可以用MmBuidlMdlForNonpagedPool 函数来初始化页码数组。对于可分页的内存,虚拟内存和物理内存之间的联系是暂时的,所以MDL的页码数组只在特定的环境和时间段有效,因为很可能其他的程序对它们进行重新分配,为了使其他的程序无法对他们进行修改和重新分配(在我们释放之前),我们就需要把这段内存锁定,防止其他程序修改,我们可以用MmProbeAndLockPages 来实现,这个函数同时还为当前的布局初始化了页码数组。当我们用MmUnlockPages 来释放被锁定的内存时,页码数组也会随之无效。 (编辑:威海站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|