WDF驱动开发-I/O请求的处理(三)

news2024/11/8 12:18:43
创建框架请求对象

框架请求对象表示 I/O 管理器已发送到驱动程序的 I/O 请求。 基于框架的驱动程序通过调用 框架请求对象方法来处理每个 I/O 请求。

每个 I/O 请求都包含一个 WDM I/O 请求数据包 (IRP 结构) ,但基于框架的驱动程序通常不需要访问 IRP 结构。

大多数框架请求对象由框架创建,但驱动程序也可以创建请求对象。

由框架创建的请求对象

当基于框架的驱动程序从 I/O 管理器接收 I/O (IRP) 的 I/O 请求数据包时,框架会截获 IRP 并创建框架请求对象。 框架将请求对象放入 I/O 队列,如果驱动程序已注册队列 的请求处理程序 ,则调用相应的处理程序。

下图演示了框架为读取操作创建请求对象时发生的步骤:

 

以下步骤对应于上图中的数字:

  1. 用户模式应用程序通过调用 Microsoft Win32 ReadFile 函数读取文件;
  2. ReadFile 函数调用在内核模式下运行的 I/O 管理器;
  3. I/O 管理器分配 IRP 结构,并将 IRP_MJ_READ 函数代码存储在 结构中;
  4. I/O 管理器调用驱动程序 x 的 DispatchRead 标准驱动程序例程,传递指向 IRP 结构的指针。 由于驱动程序 x 是基于框架的驱动程序,因此框架提供驱动程序的 DispatchRead 例程;
  5. 框架创建一个表示 IRP 结构的请求对象。 框架将请求对象添加到驱动程序的队列对象之一;
  6. 框架调用驱动程序的 EvtIoRead 请求处理程序,传递队列对象句柄和请求对象句柄;
请求驱动程序创建的对象

基于框架的驱动程序还可以创建请求对象。 例如,如果驱动程序收到一个读取或写入请求,该请求量大于驱动程序的 I/O 目标 一次可以处理的数据量,则驱动程序可能会创建请求对象。 在这种情况下,驱动程序可以将数据划分为多个较小的请求,并使用其他请求对象将这些较小的请求发送到一个或多个 I/O 目标。

若要创建请求对象,驱动程序应调用 WdfRequestCreate ,后跟初始化请求的框架对象方法,例如 WdfUsbTargetPipeFormatRequestForRead。

如果驱动程序在 WDM 调度例程中接收 WDM IRP,然后使用框架对其进行服务或转发,则该驱动程序可以调用 WdfRequestCreateFromIrp。

使用请求对象上下文

每个框架请求对象(无论是由框架创建还是由驱动程序创建)都可以包含驱动程序定义的上下文空间。 当基于框架的驱动程序初始化框架设备对象时,驱动程序可以调用 WdfDeviceInitSetRequestAttributes 来指定描述设备请求对象的上下文空间的 WDF_OBJECT_ATTRIBUTES 结构。

框架为请求对象分配上下文空间,如下所示:

  • 当框架为驱动程序创建请求对象时,它会使用驱动程序在调用 WdfDeviceInitSetRequestAttributes 时指定的大小来分配上下文空间;
  • 如果驱动程序通过调用 WdfRequestCreate 创建其他请求对象,可以通过提供WDF_OBJECT_ATTRIBUTES结构来指定上下文大小;
请求所有权

当 I/O 管理器将 I/O 请求发送到基于框架的驱动程序时,框架会截获该请求并创建框架请求对象。 框架“拥有”请求对象,因为只有框架可以访问请求并对该对象执行操作。

框架创建请求对象后,会将该对象置于驱动程序的 I/O 队列之一中。 框架继续拥有请求对象,直到它从队列中删除请求并将其传递给驱动程序。

驱动程序 收到 请求对象后,它拥有该请求。 驱动程序可以通过句柄访问请求对象,并对该对象执行操作。 虽然驱动程序拥有请求对象,但它可以 重新排队、 完成、 取消或 转发 请求,之后它不再拥有请求对象,并且无法访问它。

当请求对象的所有权在驱动程序和框架之间传递时,对象句柄的值不会更改。 例如,如果驱动程序从 I/O 队列接收请求,将其重新排队到其他队列,然后再次接收请求,则句柄的值不会更改。 同样,如果驱动程序将请求转发到 I/O 目标,然后收到 I/O 目标已完成请求的通知,则驱动程序的通知回调函数将接收驱动程序提供给 I/O 目标的相同句柄值。

将 I/O 请求重新排队

驱动程序可以重新排队从 I/O 队列获取的 I/O 请求。 驱动程序可以将 I/O 请求重新排到驱动程序为同一设备创建的另一个 I/O 队列。 此外, 总线驱动程序 可以将 I/O 请求从子设备的 I/O 队列重新排队到父设备的 I/O 队列。

将 I/O 请求重新排队到设备的不同 I/O 队列

在驱动程序的请求处理程序从驱动程序的 I/O 队列收到 I/O 请求后,驱动程序可以调用 WdfRequestForwardToIoQueue 将请求重新排队到另一个队列。

例如,如果希望驱动程序在处理请求之前将资源分配给请求,驱动程序的 EvtIoDefault 回调函数可以接收所有请求,将资源信息存储在每个请求的上下文内存中,然后调用 WdfRequestForwardToIoQueue 将每个请求重新排队到其他队列。

如果驱动程序调用 WdfRequestForwardToIoQueue 来重新排队驱动程序从使用顺序 调度方法的 I/O 队列中获取的 I/O 请求,则框架会将下一个 I/O 请求从顺序队列传递到驱动程序,而无需等待重新排队的请求完成。

如果驱动程序使用手动调度方法,它可以调用 WdfRequestRequeue 方法,将 I/O 请求返回到驱动程序从中获取它的 I/O 队列的头。 调用 WdfRequestRequeue 后,驱动程序对 WdfIoQueueRetrieveNextRequest 的下一次调用将检索重新排队的请求。

将 I/O 请求重新排队到父设备的 I/O 队列

父设备的函数驱动程序可以充当 总线驱动程序 ,该驱动程序 枚举 父设备的子设备,并为子 设备创建物理设备对象 (PDO) 。 此类驱动程序有时可以接收父设备必须处理的子设备的 I/O 请求。

例如,协议总线 ((如 USB) )通常控制分配给每个已连接设备的硬件资源。 因此,父总线的函数驱动程序通常处理每个子设备的 I/O 操作。 当 I/O 管理器将 I/O 请求发送到其中一个子 设备的设备堆栈 时,总线的函数驱动程序会在子设备的某个 I/O 队列中接收 I/O 请求,因为该驱动程序创建了子设备的 PDO。 在驱动程序可以在父总线设备的上下文中处理 I/O 请求之前,它必须将子设备的 I/O 队列中的 I/O 请求重新排队到属于父设备的 I/O 队列。

但是,驱动程序无法调用 WdfRequestForwardToIoQueue 将请求从子队列移动到父队列。 由于 I/O 管理器为父设备和子设备创建单独的设备堆栈,因此必须先将基础 WDM 设备对象从表示子设备的对象更改为表示父设备的对象。

在 KMDF 版本 1.9 之前,驱动程序只能通过创建远程 I/O 目标、增加子设备的设备堆栈大小以及指定正确的 WDM 设备对象,将 I /O 请求从子设备发送到其父设备。

从 KMDF 版本 1.9 开始,驱动程序可以在创建子设备之前调用 WdfPdoInitAllowForwardingRequestToParent ,然后调用 WdfRequestForwardToParentDeviceIoQueue 以将请求从子级 I/O 队列重新排队到父队列。 如果驱动程序使用WdfPdoInitAllowForwardingRequestToParent 和 WdfRequestForwardToParentDeviceIoQueue,框架将增加子级的设备堆栈大小,并将正确的 WDM 设备对象分配给 I/O 请求。

完成 I/O 请求

每个基于框架的驱动程序最终都必须完成它从框架收到的每个 I/O 请求。 驱动程序通过调用请求对象的 WdfRequestComplete、 WdfRequestCompleteWithInformation 或 WdfRequestCompleteWithPriorityBoost 方法完成请求。

何时完成请求

驱动程序在确定以下情况之一为 true 时,必须完成请求:

  • 请求的 I/O 操作已成功完成;
  • 请求的 I/O 操作已启动,但在完成之前失败;
  • 请求的 I/O 操作不受支持,或在收到操作时无效,无法启动;
  • 请求的 I/O 操作已取消;

如果驱动程序通过在设备上创建 I/O 活动来为 I/O 请求提供服务,驱动程序通常会从其 EvtInterruptDpc 或 EvtDpcFunc 回调函数调用 WdfRequestComplete。

如果驱动程序收到不受支持的请求或无效请求,它通常从接收请求的请求处理程序调用 WdfRequestComplete。

如果 I/O 操作被取消,驱动程序通常会从其 EvtRequestCancel 回调函数调用 WdfRequestComplete。

如果驱动程序将 I/O 请求 转发 到 I/O 目标,驱动程序在 I/O 目标完成请求后完成请求,如下所示:

  • 如果驱动程序将 I/O 请求 同步 转发到 I/O 目标,则驱动程序对 I/O 目标的调用仅在较低级别的驱动程序完成请求 后返回,除非发生错误。 I/O 目标返回后,驱动程序必须调用 WdfRequestComplete;
  • 如果驱动程序 以异步方式转发 I/O 请求,你将希望在较低级别的驱动程序完成请求时收到通知。 如果驱动程序注册 CompletionRoutine 回调函数,框架会在 I/O 目标完成请求后调用此回调函数。 CompletionRoutine 回调函数通常调用 WdfRequestComplete;

若要注册 CompletionRoutine 回调函数,驱动程序必须先调用 WdfRequestSetCompletionRoutine ,然后才能将 I/O 请求转发到 I/O 目标。

如果在 I/O 目标完成异步转发的 I/O 请求时不需要通知驱动程序,则驱动程序不必注册 CompletionRoutine 回调函数。 相反,驱动程序可以在调用 WdfRequestSend 时设置WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET标志。 在这种情况下,驱动程序不调用 WdfRequestComplete。

驱动程序无法完成它通过调用 WdfRequestCreate 或 WdfRequestCreateFromIrp 创建的 I/O 请求。 相反,驱动程序必须调用 WdfObjectDelete 来删除请求对象,通常是在 I/O 目标完成请求之后。

例如,驱动程序可能会收到读取或写入请求,该请求的数据量大于驱动程序的 I/O 目标一次可以处理的数据量。 驱动程序必须将数据划分为多个较小的请求,并将这些较小的请求发送到一个或多个 I/O 目标。 处理这种情况的方法包括:

  • 调用 WdfRequestCreate 以创建表示较小请求的单个附加请求对象:驱动程序可以同步将此请求发送到 I/O 目标。 较小请求的 CompletionRoutine 回调函数可以调用 WdfRequestReuse ,以便驱动程序可以重用请求并再次将其发送到 I/O 目标。 在 I/O 目标完成最后一个较小的请求后, CompletionRoutine 回调函数可以调用 WdfObjectDelete 以删除驱动程序创建的请求对象,驱动程序可以调用 WdfRequestComplete 来完成原始请求;
  • 调用 WdfRequestCreate 以创建表示较小请求的其他几个请求对象:驱动程序的 I/O 目标可以异步处理这些多个较小的请求。 驱动程序可以为每个较小的请求注册 CompletionRoutine 回调函数。 每次调用 CompletionRoutine 回调函数时,它都可以调用 WdfObjectDelete 来删除驱动程序创建的请求对象。 I/O 目标完成所有较小的请求后,驱动程序可以调用 WdfRequestComplete 来完成原始请求;
提供完成信息

驱动程序完成请求后,可以选择提供其他驱动程序可以访问的一些附加信息。 例如,驱动程序可以提供为读取或写入请求传输的字节数。 若要提供此信息,驱动程序可以执行以下任一操作:

  • 在调用 WdfRequestComplete 之前调用 WdfRequestSetInformation;
  • 调用 WdfRequestCompleteWithInformation;
获取完成信息

若要获取有关另一个驱动程序已完成的 I/O 请求的信息,驱动程序可以:

  • 调用 WdfRequestGetStatus 以获取较低级别驱动程序在调用 WdfRequestComplete 时指定的完成状态值;
  • 调用 WdfRequestGetCompletionParams 以获取包含有关已完成请求的其他信息的 WDF_REQUEST_COMPLETION_PARAMS 结构,例如表示请求缓冲区的内存对象的句柄或特定于总线的信息。驱动程序只能在调用 WdfRequestSend 以同步或异步方式将 I/O 请求发送到 I/O 目标之后调用 WdfRequestGetCompletionParams。 驱动程序调用将 I/O 请求仅 同步发送到 I/O 目标的方法之一(例如 WdfIoTargetSendReadSynchronously) 后,不得调用 WdfRequestGetCompletionParams;
  • 调用 WdfRequestGetInformation 以获取较低级别驱动程序在调用 WdfRequestSetInformation 或 WdfRequestCompleteWithInformation 时指定的附加 I/O 完成信息,如果驱动程序堆栈中的驱动程序提供此类信息;

如果驱动程序以同步方式发送 I/O 请求,则它通常在同步调用返回后调用 WdfRequestGetStatus、 WdfRequestGetCompletionParams 和 WdfRequestGetInformation 。 如果驱动程序异步发送 I/O 请求,它通常从 CompletionRoutine 回调函数中调用这些方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1813636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI大模型】Transformers大模型库(八):大模型微调之LoraConfig

目录 一、引言 二、LoraConfig配置参数 2.1 概述 2.2 LoraConfig参数说明 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库,为huggingface上数以万计的预训练大模型提供预测、训练等服务。 🤗 Transformers …

3.2 窗口滚动条

本节讲述窗口滚动条的简单使用方法。如果窗口客户区的内容太多,为了方便浏览窗口客户区的所有内容,就需要在创建窗口时添加窗口垂直或水平滚动条样式。窗口过程处理WM_CREATE消息时初始化滚动条的位置和滚动范围。窗口过程处理WM_VSCROLL或WM_HSCROLL消息…

讲透计算机网络知识(实战篇)01——计算机网络和协议

一、计算机网络和协议 1、网络和互联网络 1.1 网络、互联网、Internet 用交换机、集线器连接在一起的计算机构成一个网络。 用路由器连接多个网络,形成互联网。 全球最大的互联网:Internet。 1.2 网络举例 家庭互联网 图中的无线拨号路由器既是路由…

281 基于matlab的路径规划GUI交互

基于matlab的路径规划GUI交互。包括蚁量系统、蚁周系统、蚁密系统、蚁群系统、免疫混合算法。11种路径规划数据,最多225个规划点。蚁群和免疫算法的参数可进行设置,使得效果最佳。动态显示可视化规划结果。程序已调通,可直接运行。

防止Selenium被检测 Google Chrome 125

背景 最近在使用selenium自动播放学习课程,相信大家也有一些类似的使用场景。 能自动化的事情,绝不自己干。 为防止被检测是机器人做题,刷视频,需要做一些小调整。 先来看作为服务方维护者,是如何检测是Selenium打…

8.transformers量化

Transformers 核心设计Auto Classes Transformers Auto Classes 设计:统一接口、自动检索 AutoClasses 旨在通过全局统一的接口 from_pretrained() ,实现基于名称(路径)自动检索预训练权重(模 型)、配置文件、词汇表等所有与模型相关的抽象。 灵活扩展的配置AutoConfig…

uniapp地图自定义文字和图标

这是我的结构&#xff1a; <map classmap id"map" :latitude"latitude" :longitude"longitude" markertap"handleMarkerClick" :show-location"true" :markers"covers" /> 记住别忘了在data中定义变量…

pip 配置缓存路径

在windows操作平台&#xff0c;默认情况&#xff0c;pip下使用的系统目录 C:\Users\用名名称\AppData\Local\pip C盘是系统盘&#xff0c;如果常常使用pip安装会占用大量的空间很快就满&#xff0c;这时候就有必要变更一下缓存保存路径了。 pip 配置缓存路径&#xff1a; Win…

【NoSQL数据库】Redis Cluster集群(含redis集群扩容脚本)

Redis Cluster集群 Redis ClusterRedis 分布式扩展之 Redis Cluster 方案功能数据如何进行存储 redis 集群架构集群伸缩向集群中添加一个新的master节点&#xff0c;并向其中存储 num10 .脚本对redis集群扩容缩容&#xff0c;脚本参数为redis集群&#xff0c;固定从6001移动200…

创建第一个Springboot项目HelloWorld

目录 一、准备工作 一、创建springboot项目 三、使用git上传到代码仓库gitee 四、git使用过程问题总结 一、准备工作 安装jdk&#xff1a;8u201&#xff08;可以使用高一点的版本&#xff09; jdk所有版本下载&#xff1a;Java Archive | Oracle 安装maven&#xff1a;不用…

Kafka生产者消息发送流程原理及源码分析

Kafka是一个分布式流处理平台,它能够以极高的吞吐量处理数据。在Kafka中,生产者负责将消息发送到Kafka集群,而消费者则负责从Kafka集群中读取消息。本文将探讨Kafka生产者消息发送流程的细节,包括消息的序列化、分区分配、记录提交等关键步骤。 先看一个生产者发送消息的代…

【五】Linux软件仓库Yum源--SSH远程控制--SCP远程传输

RPM&#xff08;红帽软件包管理器&#xff09; RPM建立统一的数据库文件&#xff0c;记录软件信息并分析依赖关系。目前RPM的优势已经被公众所认可&#xff0c;使用范围也已不局限在红帽系统中了。常见RPM命令如下&#xff1a; 安装软件 rpm -ivh file…

数据结构(DS)学习笔记(二):数据类型与抽象数据类型

参考教材&#xff1a;数据结构C语言版&#xff08;严蔚敏&#xff0c;杨伟民编著&#xff09; 工具&#xff1a;XMind、幕布、公式编译器 正在备考&#xff0c;结合自身空闲时间&#xff0c;不定时更新&#xff0c;会在里面加入一些真题帮助理解数据结构 目录 1.1数据…

【DevOps】 什么是容器 - 一种全新的软件部署方式

目录 引言 一、什么是容器 二、容器的工作原理 三、容器的主要特性 四、容器技术带来的变革 五、容器技术的主要应用场景 六、容器技术的主要挑战 七、容器技术的发展趋势 引言 在过去的几十年里,软件行业经历了飞速的发展。从最初的大型机时代,到后来的个人电脑时代,…

neo4j 3.5.5版本创建新的数据库

neo4j 3.5.5版本创建新的数据库 1.找到neo4j的conf文件 点进去 2.点击neo4j.conf 选择记事本打开 3.把graph.db换成自己想要创建的数据库名称 4.打开neo4j服务 出现新的数据库

信息系统项目管理师0151:输出(9项目范围管理—9.4收集需求—9.4.3输出)

点击查看专栏目录 文章目录 9.4.3 输出9.4.3 输出 需求文件 需求文件描述各种单一需求将如何满足项目相关的业务需求。一开始可能只有高层级的需求,然后随着有关需求信息的增加而逐步细化。只有明确的(可测量和可测试的)、可跟踪的、完整的、相互协调的,且主要干系人愿意认…

FreeRTOS学习笔记-基于stm32(14)内存管理

一、FreeRTOS 内存管理简介 FreeRTOS有两种方法来创建任务&#xff0c;队列&#xff0c;信号量等&#xff0c;一种动态一种静态。静态方法需要手动定义任务堆栈。使用动态内存管理的时候 FreeRTOS 内核在创建任务、队列、信号量的时候会动态的申请 RAM。 我们在移植FreeRTOS时可…

采用java+springboot+vue+uniapp自主研发的智慧城管源码,城管综合执法平台源代码

智慧城管执法平台源码&#xff0c;PCAPP端全套源码&#xff0c;城管综合执法系统源码。 智慧城管系统拥有自主版权&#xff0c;项目落地案例&#xff0c;有演示&#xff0c;适合二次开发项目使用。 智慧城管执法系统旨在提高城市管理效率&#xff0c;涵盖了城市管理中的很多业务…

RabbitMQ从入门到入土

同步与异步 同步调用 优势&#xff1a; 时效性强&#xff0c;等到结果后就返回 问题&#xff1a; 扩展性差 性能下降 级联失败问题 异步调用 优势&#xff1a; 耦合度低&#xff0c;扩展性强 无需等待&#xff0c;性能好 故障隔离&#xff0c;下游服务故障不影响上游 缓…

探索乡村振兴新模式:发挥科技创新在乡村振兴中的引领作用,构建智慧农业体系,助力美丽乡村建设

随着科技的不断进步&#xff0c;乡村振兴工作正迎来前所未有的发展机遇。科技创新作为推动社会发展的重要力量&#xff0c;在乡村振兴中发挥着越来越重要的引领作用。本文旨在探讨如何发挥科技创新在乡村振兴中的引领作用&#xff0c;通过构建智慧农业体系&#xff0c;助力美丽…