I/O 队列的示例使用

对于连接到系统并由特定驱动程序支持的每个设备,驱动程序可以使用 I/O 队列和 请求处理程序的以下组合:

  • 单个默认 I/O 队列和单个请求处理程序 EvtIoDefault。 框架会将设备的所有请求传递到默认队列,并将调用驱动程序的 EvtIoDefault 处理程序将每个请求传递到驱动程序。

  • 单个默认 I/O 队列和多个请求处理程序,例如 EvtIoReadEvtIoWriteEvtIoDeviceControl。 框架会将设备的所有请求传送到默认队列。 它将调用驱动程序的 EvtIoRead 处理程序来传递读取请求,调用 EvtIoWrite 处理程序来传递写入请求,并调用 EvtIoDeviceControl 处理程序来传递设备 I/O 控制请求。

  • 多个 I/O 队列,例如一个用于读取请求,另一个用于写入请求。 对于每个队列,驱动程序只提供一个请求处理程序,因为队列只接收一种类型的请求。

  • 多个 I/O 队列,每个队列具有多个请求处理程序。

某些示例方案包括:

单个顺序 I/O 队列

如果要为一次只能为一个读取和写入请求提供服务的磁盘驱动器编写函数驱动程序,则每个设备只需要一个 I/O 队列。

驱动程序可以使用框架在驱动程序调用 WdfIoQueueCreate 时创建的默认 I/O 队列,并在队列的 WDF_IO_QUEUE_CONFIG 结构中将 DefaultQueue 设置为 TRUE。 在WDF_IO_QUEUE_CONFIG结构中,驱动程序还应指定:

  • WdfIoQueueDispatchSequential 作为调度方法,因此默认 I/O 队列将同步向驱动程序传递 I/O 请求。

  • 单个事件回调函数 EvtIoDefault 将接收所有 I/O 请求。

每次 I/O 请求在驱动程序的默认 I/O 队列中可用时,框架将通过调用驱动程序的 EvtIoDefault 请求处理程序将请求传递给驱动程序。 如果队列中出现另一个请求,框架将不会传递该请求,直到驱动程序为先前传递的请求调用 WdfRequestComplete

多个顺序 I/O 队列和手动队列

请考虑具有以下特征的串行端口设备:

  • 它可以同时执行一个读取操作和一个写入操作。

  • 它不能异步执行多个读取或写入操作。

  • 它可以接收设备 I/O 控制请求以获取状态信息。 设备的驱动程序可能需要很长时间才能完成其中一些请求 (例如) 等待状态更改的请求。

此设备的函数驱动程序可以为每个设备使用多个顺序 I/O 队列。 驱动程序将调用 WdfIoQueueCreate 三次:一次用于创建默认队列,两次调用创建两个额外的 I/O 队列。 在每个队列的 WDF_IO_QUEUE_CONFIG 结构中,驱动程序应指定:

  • WdfIoQueueDispatchSequential 作为每个队列的调度方法,以便框架以同步方式向驱动程序传递 I/O 请求。

  • 每个队列的不同 请求处理程序 (EvtIoDefaultEvtIoReadEvtIoWrite) ,它们将接收队列的 I/O 请求。

调用 WdfIoQueueCreate 后,驱动程序可以调用 WdfDeviceConfigureRequestDispatching 两次 - 将所有读取请求转发到其他队列之一,并将所有写入请求转发到另一个队列。

使用此配置,设备的默认 I/O 队列 EvtIoDefault 回调函数将仅接收设备 I/O 控制请求以获取状态信息。

如果驱动程序必须长时间保留状态请求,则可以创建第四个队列,并将 WdfIoQueueDispatchManual 指定为调度方法。 当驱动程序收到它必须等待的信息的请求时,它可以将请求置于此额外的队列中,直到状态信息变得可用。 然后,驱动程序可以从队列中检索请求并完成该请求。 同时,默认队列可以将另一个请求传递给驱动程序。

单个并行 I/O 队列

IDE 磁盘控制器可以重叠某些 I/O 操作,但不能重叠其他 I/O 操作。 例如,当控制器在一个磁盘上处理读取或写入操作时,它可以将 seek 命令发送到另一个磁盘。 另一方面,不支持多个同时读取和写入命令。

此控制器的函数驱动程序必须检查每个 I/O 请求。 如果驱动程序收到 seek 命令,则必须确定是否可以处理 seek 命令。 在以下的情况下,无法处理 seek 命令:

  • 指定的磁盘驱动器已忙。

  • 磁盘驱动器正在格式化,因此,其他驱动器不能处于活动状态。

对于连接到控制器的每个设备,驱动程序可以调用 WdfIoQueueCreate 来创建默认 I/O 队列。 在每个队列的 WDF_IO_QUEUE_CONFIG 结构中,驱动程序应指定:

  • WdfIoQueueDispatchParallel 作为每个队列的调度方法,以便框架以异步方式向驱动程序传递 I/O 请求。

  • 每个队列的 EvtIoDefault 事件回调函数将接收队列的 I/O 请求。

使用此配置,将单个并行 I/O 队列分配给每个设备。 驱动程序必须检查框架从每个 I/O 队列传递的每个 I/O 请求。 如果驱动程序可以立即处理请求,则它会这样做。 否则,驱动程序调用 WdfIoQueueStop,这会导致框架停止传递请求,直到驱动程序调用 WdfIoQueueStart

多个并行 I/O 队列

SCSI 主机适配器是支持异步重叠 I/O 操作的设备示例。 最多可将 32 台设备连接到适配器。 请考虑具有以下配置的系统:

  • 某些连接到 SCSI 适配器的设备支持“重新选择”,有些则不支持。 如果 SCSI 设备支持重新选择,则在 I/O 操作期间,设备可以暂时释放适配器,以便适配器可以为其他设备提供服务。 第一个设备随后会重新选择自身以完成其操作。

  • SCSI 适配器使用硬件邮箱在驱动程序和设备之间传递请求和响应。 如果设备已准备好接收请求,但没有可用的邮箱,则设备必须等待。

为了获得最佳性能,此 SCSI 主机适配器的函数驱动程序应在 I/O 请求可用后立即从框架接收这些请求。 驱动程序必须检查每个请求,并确定它是否可以立即启动,或者必须推迟到设备和资源 ((如邮箱内存) )可用。

驱动程序可能应使用多个并行 I/O 队列。 对于连接到适配器的每个设备,驱动程序将调用 WdfIoQueueCreate 以创建默认 I/O 队列。 在每个队列的 WDF_IO_QUEUE_CONFIG 结构中,驱动程序应指定:

  • WdfIoQueueDispatchParallel 作为每个队列的调度方法,以便框架以异步方式向驱动程序传递 I/O 请求。

  • 每个队列的 EvtIoDefault 事件回调函数将接收队列的 I/O 请求。

每个 I/O 队列的 EvtIoDefault 回调函数都必须检查队列的 I/O 请求,因为它们已传递,并确定每个请求是否可以立即得到服务。 如果设备和系统资源可用,驱动程序将启动 I/O 操作。 如果设备或资源不可用,驱动程序必须调用 WdfIoQueueStop 来停止传递其他请求,直到可以处理当前请求。

(可选)驱动程序可以调用 WdfIoQueueCreate ,为每个设备创建其他队列。 然后,驱动程序可以调用 WdfRequestForwardToIoQueue ,将某些类型的请求重新排队到其他队列。 当框架从其他队列传递请求时,驱动程序可以在必要时在该队列(而不是默认队列)上调用 WdfIoQueueStop,从而最大程度地减少延迟传递的请求数或类型。