调度例程的约束

注意

为了获得最佳可靠性和性能,请使用支持筛选器管理器的 文件系统微筛选器驱动程序 ,而不是旧的文件系统筛选器驱动程序。 若要将旧驱动程序移植到微筛选器驱动程序,请参阅 移植旧版筛选器驱动程序指南

以下指南简要讨论了旧文件系统筛选器驱动程序如何避免调度例程中的常见编程错误。

有关分页 I/O 中使用的 IRP 类型的信息,请参阅 Dispatch 例程 IRQL 和线程上下文

  • 分页 I/O 路径中的调度例程不应在大于 APC_LEVEL 的任何 IRQL 上调用 IoCallDriver 。 无法在DISPATCH_LEVEL执行分页 I/O (或任何 I/O) ,因为系统使用 APC 来处理 I/O 完成,因此你永远不会看到操作已完成。 如果调度例程引发 IRQL,则必须在调用 IoCallDriver 之前将其降低。

  • 在所有情况下,调度例程在APC_LEVEL调用 IoCallDriver 并不一定安全。 请参阅 调度例程 IRQL 和线程上下文 ,以确定是否可以处于APC_LEVEL或必须处于PASSIVE_LEVEL。

  • 分页 I/O 路径中的调度例程(如读取和写入)无法安全地调用要求调用方在 IRQL PASSIVE_LEVEL上运行的任何内核模式例程。

  • 分页文件 I/O 路径中的调度例程无法安全地调用任何需要调用方在 IRQL < DISPATCH_LEVEL运行的内核模式例程。

  • 不在分页 I/O 路径中的调度例程不应在大于 PASSIVE_LEVEL 的任何 IRQL 上调用 IoCallDriver 。 如果调度例程引发 IRQL,则必须在调用 IoCallDriver 之前将其降低。

处理 IRP 的约束

  • 如果 IRP 参数包含任何用户空间地址,则必须在使用这些地址之前对其进行验证。 有关详细信息,请参阅 缓冲 I/O 中的错误

  • 此外,如果 IRP 包含从 32 位平台发送到 64 位平台的 IOCTL 或 FSCTL 缓冲区,则可能需要删除缓冲区内容。 有关详细信息,请参阅 在 64 位驱动程序中支持 32 位 I/O

  • 与文件系统不同,文件系统筛选器驱动程序不应调用 FsRtlEnterFileSystemFsRtlExitFileSystem ,除非在调用 ExAcquireFastMutexUnsafeExAcquireResourceExclusiveLite 之前调用。 FsRtlEnterFileSystemFsRtlExitFileSystem 禁用大多数文件系统所需的普通内核 APC。

  • 无法从分页 I/O 路径发出其他 IRP。 可以将发出 I/O 的工作线程排队,但不得同步等待该工作线程完成,因为等待会导致死锁。

完成 IRP 的约束

  • 完成 IRP 时,文件系统筛选器驱动程序应仅使用成功和错误状态值。

  • 虽然 STATUS_PENDING 是一个成功的 NTSTATUS 值,但使用 STATUS_PENDING 完成 IRP 是一个编程错误。

  • 调度例程调用 IoCompleteRequest 后,IRP 指针不再有效,无法安全地取消引用。

设置完成例程的约束

有关设置完成例程的信息,请参阅 使用完成例程

  • 当调度例程调用 IoSetCompletionRoutine 时,它可以选择性地将 Context 指针传递给结构,供处理给定 IRP 时使用的完成例程使用。 必须从非分页池分配此结构,因为完成例程可以DISPATCH_LEVEL调用 IRQL。

  • 如果调度例程设置可能返回STATUS_MORE_PROCESSING_REQUIRED的完成例程,则必须执行以下操作之一,以防止 I/O 管理器过早完成 IRP:

向下传递 IRP 的约束

  • 调度例程调用 IoCallDriver 后,IRP 指针不再有效,并且无法安全地取消引用,除非调度例程等待完成例程发出已调用的信号。

  • 从文件系统筛选器驱动程序调用 PoCallDriver 是一个编程错误。 (PoCallDriver 用于将IRP_MJ_POWER请求传递给较低级别的驱动程序。文件系统筛选器驱动程序从不接收IRP_MJ_POWER requests.)

返回状态的约束

  • 除非完成 IRP,否则未设置完成例程的调度例程应始终返回 IoCallDriver 返回的 NTSTATUS 值。 除非此值STATUS_PENDING,否则它必须与完成 IRP 的驱动程序设置的 Irp-IoStatus.Status> 的值匹配。

  • IoCallDriver 返回STATUS_PENDING时,调度例程还应返回STATUS_PENDING,除非它等待完成例程发出事件信号。

  • 当调度例程将 IRP 发布到辅助角色队列以供以后处理时,它应将 IRP 标记为挂起并返回STATUS_PENDING。

  • 当调度例程设置一个可能将 IRP 发布到辅助角色队列以供以后处理的完成例程时,它应将 IRP 标记为挂起并返回STATUS_PENDING。

  • 标记 IRP 挂起的调度例程必须返回STATUS_PENDING。

  • 无法将 Oplock 操作 (发布到辅助角色队列) ,并且调度例程无法为其返回STATUS_PENDING。

将 IRP 发布到工作队列的约束

  • 如果调度例程将 IRP 发布到工作队列,则必须在将每个 IRP 发布到辅助角色队列之前调用 IoMarkIrpPending 。 否则,IRP 可能会取消排队,由另一个驱动程序例程完成,并在调用 IoMarkIrpPending 之前由系统释放,从而导致崩溃。