访问 UMDF 1.x 驱动程序中的数据缓冲区

警告

UMDF 2 是 UMDF 的最新版本,取代了 UMDF 1。 所有新的 UMDF 驱动程序都应使用 UMDF 2 编写。 不会向 UMDF 1 添加任何新功能,并且较新版本的 Windows 10 上对 UMDF 1 的支持有限。 通用 Windows 驱动程序必须使用 UMDF 2。

有关详细信息,请参阅使用 UMDF 入门

有关访问 UMDF 2 的数据缓冲区的信息,请参阅 访问 WDF 驱动程序中的数据缓冲区

当驱动程序收到读取、写入或设备 I/O 控制请求时,请求对象包含输入缓冲区或输出缓冲区,或者同时包含两者。 (几个设备 I/O 控制请求提供两个输入、两个输出或两个输入/输出缓冲区。)

输入缓冲区包含驱动程序所需的信息。 对于写入请求,此信息通常是函数驱动程序必须发送到设备的数据。 对于设备 I/O 控制请求,输入缓冲区可能包含指示驱动程序必须执行的操作类型的信息。

输出缓冲区从驱动程序接收信息。 对于读取请求,此信息通常是函数驱动程序从设备接收的数据。 对于设备 I/O 控制请求,输出缓冲区可能会收到请求的 I/O 控制代码指定的状态或其他信息。

驱动程序用于访问请求数据缓冲区的技术可能取决于驱动程序用于访问设备数据缓冲区的方法。 UMDF 支持以下缓冲区访问方法:

基于 UMDF 的驱动程序无法使用第三个访问方法( 既不称为缓冲也不直接 I/O),但 UMDF 可以将某些 I/O 请求从“两者”方法转换为 UMDF 版本支持的方法。

在大多数情况下,基于 UMDF 的驱动程序调用相同的 UMDF 对象方法来访问数据缓冲区,无论 UMDF 和驱动程序使用的是缓冲 I/O 还是直接 I/O。 与缓冲区 I/O 相比,直接 I/O 通常提供的性能更好。

本主题的以下部分介绍了:

指定首选缓冲区访问方法

UMDF 1.9 和更高版本同时支持缓冲和直接 I/O 访问方法。 驱动程序可以通过调用 IWDFDeviceInitialize2::SetIoTypePreference 来指定你希望用于设备的所有读取、写入和设备 I/O 控制请求的访问方法,然后再调用 IWDFDriver::CreateDevice 来创建设备对象。 例如,如果驱动程序为其设备之一的读取和写入请求指定仅缓冲 I/O 方法的首选项,则 UMDF 驱动程序主机进程 在向该设备的驱动程序传递读取和写入请求时使用缓冲 I/O 方法。 如果驱动程序指定了直接 I/O 的首选项,则 UMDF 可以 (,但可能不会) 使用直接 I/O。 有关 UMDF 何时使用直接 I/O 的详细信息,请参阅 UMDF 如何为 I/O 请求选择缓冲区访问方法

对于驱动程序支持的每个设备,驱动程序可以为缓冲 I/O、直接 I/O 或设备指定缓冲或直接 I/O 的首选项。 驱动程序可以为读取和写入请求指定一种类型的访问方法,并为设备 I/O 控制请求指定另一种类型的访问方法。 如果驱动程序未指定访问方法首选项,则 UMDF 将使用缓冲方法。

对于设备 I/O 控制请求,I/O 控制代码 (IOCTL) 指定缓冲区访问方法。 (有关 IOCTL 如何指定访问方法的详细信息,请参阅 定义 I/O 控制代码。) 但是,UMDF 使用的访问方法可能与 IOCTL 指定的访问方法不匹配。

指定缓冲区检索模式

在版本 1.9 之前的 UMDF 版本中,UMDF 始终通过在 UMDF 收到 I/O 请求后立即将缓冲区复制到 UMDF 驱动程序主机进程) ,使驱动程序 (可以使用 I/O 请求的缓冲区。 此缓冲区检索模式称为 即时检索。 如果发生故障,UMDF 使用失败状态值完成 I/O 请求,并且不会将 I/O 请求传递给驱动程序。

UMDF 1.9 及更高版本支持即时检索和 延迟检索 模式。 延迟检索模式推迟将 I/O 请求的缓冲区复制到驱动程序主机进程,直到驱动程序尝试访问缓冲区。 如果发生故障,缓冲区访问函数会将错误状态值返回给驱动程序。

驱动程序可以为每个设备调用 IWDFDeviceInitialize2::SetIoTypePreference 时指定缓冲区检索模式。 使用以下规则:

  • 如果驱动程序指定直接 I/O 访问方法,则它还必须指定延迟检索模式。 直接 I/O 仅适用于延迟检索。

  • 编写为使用 UMDF 版本 1.9 及更高版本运行的所有驱动程序都应为所有 I/O 请求指定延迟检索模式,无论驱动程序选择缓冲还是直接 I/O 访问方法。 延迟检索提供了更好的性能,因为它不会访问驱动程序不使用的缓冲区。

如果驱动程序未指定缓冲区检索模式,则 UMDF 使用即时检索。

驱动程序堆栈中所有基于 UMDF 的驱动程序都必须使用相同的检索模式。 如果某些驱动程序指定即时检索,而某些驱动程序指定延迟检索,则 UMDF 使用即时检索。

UMDF 如何为 I/O 请求选择缓冲区访问方法

驱动程序在调用 IWDFDeviceInitialize2::SetIoTypePreference 时指定的访问方法可能不是 UMDF 使用的访问方法。 UMDF 使用以下规则来确定要使用的访问方法:

  • 驱动程序堆栈中所有基于 UMDF 的驱动程序都必须使用相同的方法来访问设备的缓冲区。 如果 UMDF 确定某些驱动程序更喜欢对设备使用缓冲 I/O 或直接 I/O,而其他驱动程序则只对设备使用缓冲 I/O,则 UMDF 对所有驱动程序使用缓冲 I/O。 如果一个或多个堆栈的驱动程序只喜欢缓冲 I/O,而其他驱动程序则只喜欢直接 I/O,则 UMDF 会将事件记录到系统事件日志中,并且不会启动驱动程序堆栈。

    驱动程序可以调用 IWDFDevice2::GetDeviceStackIoTypePreference 来确定 UMDF 已分配给设备的读/写请求和 I/O 控制请求的缓冲区访问方法。

  • 在某些情况下,驱动程序在调用 IWDFDeviceInitialize2::SetIoTypePreference 时指定直接 I/O 的首选项,但为了获得最佳性能,UMDF 对一个或多个设备请求使用缓冲 I/O。 例如,如果 UMDF 将数据复制到驱动程序缓冲区的速度比映射缓冲区以便直接访问的快,则 UMDF 对小型缓冲区使用缓冲 I/O。

    (可选)可以设置REG_DWORD类型的 DirectTransferThreshold 注册表值,框架使用该值来确定框架将使用直接 I/O 的最小缓冲区大小。 通常,不需要提供此注册表值,因为框架使用可提供最佳性能的值。 DirectTransferThreshold 值位于设备的“设备参数\WUDF”子项下,该子项位于设备的硬件密钥下。

    框架使用以下规则根据 DirectTransferThreshold 中提供的值确定阈值。 提供的数字假定 PAGE_SIZE 为 4096,这在基于 Itanium 的系统上有效。

    • 如果将 DirectTransferThreshold 设置为小于或等于 8192 (或 2 * PAGE_SIZE) 的任何值,框架会将阈值设置为 8192。 框架对小于 8192 字节的缓冲区使用缓冲 I/O,对等于或大于 8192 字节的缓冲区使用直接 I/O。

    • 如果将 DirectTransferThreshold 设置为大于 8192 的任何值,框架将向上舍入到 PAGE_SIZE的下一个确切倍数。 同样,框架对小于阈值的缓冲区使用缓冲 I/O,对等于或大于阈值的缓冲区使用直接 I/O。

  • UMDF 仅对在内存页边界上开始和结束的缓冲区空间使用直接 I/O。 如果缓冲区的开头或末尾不位于页面边界上,则 UMDF 对缓冲区的这一部分使用缓冲 I/O。 换句话说,UMDF 可能同时使用缓冲 I/O 和直接 I/O 进行由多个 I/O 请求组成的大型数据传输。

  • 对于设备 I/O 控制请求,仅当 IOCTL) (I/O 控制代码指定直接 I/O 时,UMDF 才使用直接 I/O,并且仅当设备的所有基于 UMDF 的驱动程序都调用 IWDFDeviceInitialize2::SetIoTypePreference 来指定直接访问方法时。

驱动程序使用相同的请求对象方法集来访问数据缓冲区,而不考虑缓冲区访问方法。 因此,大多数驱动程序通常不需要知道 UMDF 是对 I/O 请求使用缓冲 I/O 还是直接 I/O。

驱动程序如何获取 I/O 请求的访问方法

在某些情况下,如果访问方法已知,则可以提高设备和驱动程序的性能。 在这种情况下,驱动程序可以调用 IWDFIoRequest2::GetEffectiveIoType 来获取 I/O 请求的缓冲区访问方法。

例如,请考虑通常使用直接 I/O 的高吞吐量设备。 由于它使用的是直接 I/O,因此驱动程序必须在验证参数之前将应用程序指定的参数复制到本地驱动程序内存,以确保应用程序在验证后不会修改参数。

由于驱动程序偶尔可能会收到使用缓冲 I/O 的缓冲区,并且缓冲 I/O 缓冲区已复制,因此应用程序无法修改数据,并且驱动程序不必在验证参数之前复制参数。 因此,驱动程序应检查每个请求的缓冲区访问方法,以确定在验证参数之前是否必须复制参数。

在 UMDF 驱动程序中使用缓冲 I/O

如果驱动程序使用缓冲 I/O,则 UMDF 行为因请求类型而异。 对于读取和写入请求,驱动程序主机进程会创建驱动程序可以访问的单个中间缓冲区。

对于写入请求,驱动程序主机进程在调用驱动程序堆栈之前从调用应用程序的输入缓冲区传输输入信息。 驱动程序通常从中间缓冲区读取输入信息并将其写入设备。

对于读取请求,驱动程序通常从设备读取信息并将其存储在中间缓冲区中。 驱动程序主机进程将输出数据从中间缓冲区复制到应用程序的输出缓冲区。

但是,对于设备 I/O 控制请求,驱动程序主机进程会创建两个单独的缓冲区,驱动程序可以访问这些缓冲区。 请注意,这与 WDM 和 KMDF 驱动程序的行为不同,后者使用缓冲 I/O 发送的读取、写入和设备 I/O 控制请求会导致驱动程序访问单个中间缓冲区。 在这种情况下,输出缓冲区最初不包含任何内容,驱动程序不应从中读取。 此外,驱动程序写入输入缓冲区的任何数据将被丢弃,不会返回到调用应用程序。

有关何时选择缓冲 I/O 的指南,请参阅 WDF_DEVICE_IO_TYPE

UMDF 版本 1.9 及更高版本可以支持立即或延迟检索请求缓冲区。 有关详细信息,请参阅 WDF_DEVICE_IO_BUFFER_RETRIEVAL

使用即时缓冲区检索模式的驱动程序必须使用 IWDFIoRequest::GetInputMemoryIWDFIoRequest::GetOutputMemory 来访问缓冲区。

使用延迟缓冲区检索模式的驱动程序可以通过调用 IWDFIoRequest2::RetrieveInputBufferIWDFIoRequest2::RetrieveInputMemoryIWDFIoRequest2::RetrieveOutputBufferIWDFIoRequest2::RetrieveOutputMemory 来访问缓冲区。

在 UMDF 驱动程序中使用直接 I/O

如果驱动程序使用直接 I/O,驱动程序主机进程会验证 I/O 请求的发起方 (通常) 指定的用户模式应用程序、将缓冲区空间锁定到物理内存中,然后为驱动程序提供对缓冲区空间的直接访问的可访问性。

有关何时选择直接 I/O 的指南,请参阅 WDF_DEVICE_IO_TYPE

驱动程序可以通过调用 IWDFIoRequest2::RetrieveInputBufferIWDFIoRequest2::RetrieveInputMemoryIWDFIoRequest2::RetrieveOutputBufferIWDFIoRequest2::RetrieveOutputMemory 来访问缓冲区。

在 UMDF 驱动程序中既不使用缓冲 I/O,也不使用直接 I/O

称为 非缓冲 I/O 或直接 I/O 方法 的缓冲区访问方法 (或“既不”方法,简称) 允许驱动程序直接访问应用程序的请求缓冲区指针。 基于 UMDF 的驱动程序不能使用此访问方法。

但是,某些设备 I/O 控制代码的定义 (IOCTL) 指定请求使用“两者均不”方法。 (可选)UMDF 可以将此类设备 I/O 控制请求的缓冲区访问方法转换为缓冲 I/O 或直接 I/O。 请使用以下步骤:

  1. UmdfMethodNeitherAction 指令包含在驱动程序 INF 文件的 INF DDInstall 节 中。 可以设置 指令的值,以指示 UMDF 应将使用“两者”访问方法的设备 I/O 控制请求传递给驱动程序。 (否则,UMDF 会以错误状态值完成这些 I/O 请求。)

  2. 通过使用 UMDF 为缓冲 I/O 或直接 I/O 提供的对象方法访问 I/O 请求的缓冲区

仅当确定 UMDF 可以将访问方法转换为缓冲 I/O 或直接 I/O 时,才应启用对使用“两者均不”方法的 IOCTL 请求的支持。 例如,如果 IOCTL 指定的自定义请求不符合 I/O 控制代码的缓冲区说明中所述的缓冲区规范规则,则 UMDF 无法转换缓冲区。