使用 MDL

项目

2022/09/22

2 个参与者

反馈

跨越一系列连续虚拟内存地址的 I/O 缓冲区可以分散在多个物理页上,并且这些页面可能不相干。 操作系统使用 内存描述符列表 (MDL) 描述虚拟内存缓冲区的物理页面布局。

MDL 由 一个 MDL 结构组成,后跟一组描述 I/O 缓冲区所在的物理内存的数据数组。 MDL 的大小因 MDL 描述的 I/O 缓冲区的特征而异。 系统例程可用于计算 MDL 所需的大小,并分配和释放 MDL。

MDL 结构不透明。 驱动程序应仅直接访问此结构的 NextMdlFlags 成员。 有关使用这两个成员的代码示例,请参阅以下示例部分。

MDL 的其余成员不透明。 请勿直接访问 MDL 的不透明成员。 请改用操作系统提供的以下宏对结构执行基本操作:

MmGetMdlVirtualAddress 返回 MDL 描述的 I/O 缓冲区的虚拟内存地址。

MmGetMdlByteCount 返回 I/O 缓冲区的大小(以字节为单位)。

MmGetMdlByteOffset 返回 I/O 缓冲区开头的物理页中的偏移量。

可以使用 IoAllocateMdl 例程分配 MDL。 若要释放 MDL,请使用 IoFreeMdl 例程。 或者,可以通过调用 MmInitializeMdl 例程来分配非分页内存块,然后将此内存块格式化为 MDL。

IoAllocateMdlMmInitializeMdl 都不会初始化紧跟 MDL 结构的数据数组。 对于驻留在非分页内存的驱动程序分配块中的 MDL,请使用 MmBuildMdlForNonPagedPool 初始化此数组,以描述 I/O 缓冲区所在的物理内存。

对于可分页内存,虚拟内存和物理内存之间的对应关系是临时的,因此遵循 MDL 结构的数据数组仅在某些情况下有效。 调用 MmProbeAndLockPages 将可分页内存锁定到位,并为当前布局初始化此数据数组。 在调用方使用 MmUnlockPages 例程之前,内存不会分页,此时数据数组的内容不再有效。

MmGetSystemAddressForMdlSafe 例程将指定的 MDL 描述的物理页映射到系统地址空间中的虚拟地址(如果它们尚未映射到系统地址空间)。 此虚拟地址对于可能需要查看页面以执行 I/O 的驱动程序非常有用,因为原始虚拟地址可能是用户地址,只能在其原始上下文中使用,并且可以随时删除。

请注意,使用 IoBuildPartialMdl 例程生成部分 MDL 时, MmGetMdlVirtualAddress 返回部分 MDL 的原始起始地址。 如果 MDL 最初是用户模式请求的结果创建的,则此地址是用户模式地址。 因此,该地址在发起请求的进程上下文之外没有相关性。

通常,驱动程序通过调用 MmGetSystemAddressForMdlSafe 宏来映射部分 MDL 来创建系统模式地址。 这可确保无论进程上下文如何,驱动程序都可以安全地访问页面。

当驱动程序调用 IoAllocateMdl 时,它可以通过将指向 IRP 的指针指定为 IoAllocateMdlIrp 参数,将 IRP 与新分配的 MDL 相关联。 IRP 可以有一个或多个与之关联的 MDL。 如果 IRP 有一个与之关联的 MDL,则 IRP 的 MdlAddress 成员指向该 MDL。 如果 IRP 具有与其关联的多个 MDL, 则 MdlAddress 指向与 IRP 关联的 MDL 链接列表中的第一个 MDL,称为 MDL 链。 MDL 由其 Next 成员链接。 链中最后一个 MDL 的 下一 个成员设置为 NULL

如果驱动程序调用 IoAllocateMdl 时,它会为 SecondaryBuffer 参数指定 FALSE,则 IRP 的 MdlAddress 成员设置为指向新的 MDL。 如果 SecondaryBufferTRUE,则例程会在 MDL 链的末尾插入新的 MDL。

IRP 完成后,系统会解锁并释放与 IRP 关联的所有 MDL。 系统在对 I/O 完成例程进行排队之前解锁 MDL,并在 I/O 完成例程执行后释放它们。

驱动程序可以使用每个 MDL 的 Next 成员访问链中的下一个 MDL 来遍历 MDL 链。 驱动程序可以通过更新 Next 成员手动将 MDL 插入链中。

MDL 链通常用于管理与单个 I/O 请求关联的缓冲区数组。 (例如,网络驱动程序可以在网络操作中为每个 IP 数据包使用一个缓冲区。) 数组中的每个缓冲区在链中都有自己的 MDL。 驱动程序完成请求后,它将缓冲区合并到单个大型缓冲区中。 然后,系统会自动清理请求的所有已分配的 MDL。

I/O 管理器是 I/O 请求的频繁来源。 当 I/O 管理器完成 I/O 请求时,I/O 管理器释放 IRP 并释放附加到 IRP 的任何 MDL。 其中一些 MDL 可能已由位于设备堆栈中的 I/O 管理器下的驱动程序附加到 IRP。 同样,如果驱动程序是 I/O 请求的源,驱动程序必须在 I/O 请求完成时清理 IRP 和附加到 IRP 的任何 MDL。

示例

以下代码示例是驱动程序实现的函数,可从 IRP 释放 MDL 链:

C++复制

VOID MyFreeMdl(PMDL Mdl){
    PMDL currentMdl, nextMdl;

    for (currentMdl = Mdl; currentMdl != NULL; currentMdl = nextMdl) 
    {
        nextMdl = currentMdl->Next;
        if (currentMdl->MdlFlags & MDL_PAGES_LOCKED) 
        {
            MmUnlockPages(currentMdl);
        }
        IoFreeMdl(currentMdl);
    }
} 

如果链中的 MDL 描述的物理页被锁定,则示例函数调用 MmUnlockPages 例程来解锁页面,然后再调用 IoFreeMdl 来释放 MDL。 但是,在调用 IoFreeMdl 之前,示例函数不需要显式取消映射页面。 相反, IoFreeMdl 会在释放 MDL 时自动取消映射页面。

引用

https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/using-mdls

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值