设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 创业者 手机 数据
当前位置: 首页 > 服务器 > 系统 > 正文

04内核-驱动对象(4)

发布时间:2021-03-04 21:45 所属栏目:52 来源:网络整理
导读:I/O管理器为每个IRP创建I/O栈数组,其中数组元素对应于分层驱动程序链中的每个驱动程序。每个驱动程序都拥有包中的一个栈位置,并调用 IoGetCurrentIrpStackLocation 以获得关于I/O操作的驱动程序特定信息。 这样的

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的内容。

04内核-驱动对象

IRP处理流程总结

  1. IRP被IO管理器所创建

  2. 一个IRP被发出来,可以被多个驱动对象所处理,每个驱动对象都能做出不同的处理.

    IO管理器创建IRP时,会找出所有能够处理此IRP的设备对象,并为每一个设备对象建立一个IRP栈元素,(一个驱动对象所创建的设备可以挂载到另一个驱动的设备对象链中,IoAttatchDeviceToDeviceStack)

    于是便有了IRP栈,

    通过函数IoGetCurrentIrpStackLocation能够获取到本驱动的IRP栈,

  3. 一个IRP被处理完成之后,使用IoCompleteRequest来设置IRP的完成状态,设置时,主要设置以下内容

    1. Irp.IoStatus.Status - 将完成的状态设置到此字段(成功了失败了,总之得有一个状态码),这个状态码可以使用STATUS_XXXX这系列的宏.

    2. 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来释放被锁定的内存时,页码数组也会随之无效。

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读