WDF驱动开发-内存缓冲区

驱动程序通常使用内存缓冲区向/从框架和其他驱动程序传递数据,或在本地存储信息。 WDF常见的内存缓冲区包括框架内存对象(WDFMEMORY)、 lookaside、 MDL 和 本地缓冲区。

使用框架内存对象

框架使用 内存对象 来描述驱动程序从中接收并传递给框架的内存缓冲区。 每个框架内存对象表示一个缓冲区。

若要创建内存对象,驱动程序会调用以下对象方法之一:

  • WdfMemoryCreate,用于创建内存对象并分配指定大小的内存缓冲区;
  • WdfMemoryCreatePreallocated,它为预分配的缓冲区创建内存对象;
  • WdfMemoryCreateFromLookaside,用于从 旁视列表创建内存缓冲区;

若要获取表示接收的 I/O 请求缓冲区的内存对象,驱动程序会调用 WdfRequestRetrieveInputMemory 和 WdfRequestRetrieveOutputMemory。 有关检索 I/O 请求缓冲区的详细信息,请参阅 在 Framework-Based 驱动程序中访问数据缓冲区。

若要获取内存对象缓冲区的地址和大小,驱动程序会调用 WdfMemoryGetBuffer。

若要将数据移入或移出内存对象的缓冲区,驱动程序会调用 WdfMemoryCopyFromBuffer 或 WdfMemoryCopyToBuffer。 这些对象方法检查源大小和目标大小,并防止缓冲区溢出错误。

如果驱动程序通过调用 WdfMemoryCreatePreallocated 创建内存对象,则它随后可以通过调用 WdfMemoryAssignBuffer 将不同的缓冲区分配给内存对象。

当驱动程序将 I/O 请求发送到 I/O 目标时,它通常会将输入或输出缓冲区传递给 框架 I/O 目标对象方法。 驱动程序通过传递描述缓冲区的 WDF_MEMORY_DESCRIPTOR 结构或通过传递内存对象句柄来指定缓冲区。 同步发送 I/O 请求的 I/O 目标对象方法需要 WDF_MEMORY_DESCRIPTOR 结构,异步发送 I/O 请求的方法需要内存对象句柄。

使用 Lookaside 列表

如果驱动程序需要许多大小大致相同的缓冲区,则应从 lookaside中分配它们。 驱动程序通过调用 WdfLookasideListCreate 创建lookaside。 随后,驱动程序可以通过调用 WdfMemoryCreateFromLookaside 从 lookaside 列表中获取缓冲区。

每次驱动程序调用 WdfMemoryCreateFromLookaside 时,框架都会创建一个内存对象,从 lookaside 列表中获取缓冲区,并将缓冲区分配给对象。 当驱动程序使用完这些内存对象之一后,它将调用 WdfObjectDelete,这将删除内存对象并将缓冲区空间返回到lookaside

操作系统管理分配给lookaside的内存资源。 如果驱动程序在无可用(例如驱动程序第一次调用 WdfMemoryCreateFromLookaside)时从lookaside中请求缓冲区,系统会分配缓冲区并将其分配给列表。 当驱动程序 调用 WdfObjectDelete 并将缓冲区空间返回到lookaside时,系统会在列表中保留现在未分配的缓冲区,直到驱动程序再次需要它。 系统根据需要增加lookaside的大小;例如,更频繁地请求缓冲区的驱动程序会收到较大的lookaside。 另一方面,如果驱动程序未全部使用缓冲区,系统可能会减少lookaside中的缓冲区数。

使用 MDL

某些驱动程序使用内存描述符列表 (MDL) 来描述缓冲区。 例如, (DMA) 设备的直接内存访问驱动程序必须将 MDL 传递给 WdfDmaTransactionInitialize 方法(如果它调用该方法)。

使用 MDL 的驱动程序可以通过调用 WdfRequestRetrieveInputWdmMdl 和 WdfRequestRetrieveOutputWdmMdl 来获取表示已接收 I/O 请求缓冲区的 MDL。

大多数基于框架的驱动程序不使用 MDL。

分配本地缓冲区

需要不传递给框架的本地内部缓冲区空间的驱动程序不必创建内存对象来表示缓冲区。 驱动程序可以调用 ExAllocatePoolWithTag 来分配内部缓冲区。 驱动程序使用完缓冲区后,必须调用 ExFreePoolWithTag。

但是,驱动程序还可以将内存对象用于本地缓冲区。 使用内存缓冲区而不是调用 ExAllocatePoolWithTag的一个优点是,在删除每个对象的父对象时,框架会自动删除内存对象及其缓冲区。

注意:我们讨论的 ExAllocatePool DDI 已在 Windows 10 版本 2004 中弃用,并已替换为 ExAllocatePool2 和 ExAllocatePool3。 

对齐缓冲区

驱动程序可以使用 WDF_ALIGN_SIZE_UP 或 WDF_ALIGN_SIZE_DOWN 函数来计算与指定的对齐偏移量对齐的缓冲区大小。 如果驱动程序必须分配多个连续缓冲区,并且每个缓冲区必须从地址对齐边界开始,则此计算非常有用。

内存缓冲区生命周期

内存缓冲区的生命周期跨越从创建缓冲区到删除缓冲区的时间。 下面从缓冲区几种使用方案来看它们在删除缓冲区时的影响。

在 KMDF内核模式驱动程序框架中,请求对象表示 I/O 请求。 每个请求对象都与一个或多个内存对象相关联,每个内存对象表示用于请求中的输入或输出的缓冲区。

当框架创建表示传入 I/O 请求的请求和内存对象时,它会将请求对象设置为关联内存对象的父对象。 因此,内存对象的保留期不能超过请求对象的生存期。 当基于框架的驱动程序完成 I/O 请求时,框架将删除请求对象和内存对象,使这两个对象的句柄变得无效。

但是,基础缓冲区是不同的。 根据创建缓冲区的组件及其创建缓冲区的方式,缓冲区可能具有引用计数,并且可能由内存对象拥有,也可能不具有引用计数。 如果内存对象拥有缓冲区,则缓冲区具有引用计数,并且其生存期限制为内存对象的生存期。 如果其他组件创建了缓冲区,则缓冲区的生存期与内存对象无关。

基于框架的驱动程序还可以创建自己的请求对象以发送到 I/O 目标。 驱动程序创建的请求可以重复使用驱动程序在 I/O 请求中收到的现有内存对象。 经常向 I/O 目标发送请求的驱动程序可以重复使用它创建 的请求对象 。

了解请求对象、内存对象和基础缓冲区的生存期对于确保驱动程序不会尝试引用无效句柄或缓冲区指针非常重要。

请考虑以下使用场景:

方案 1:驱动程序从 KMDF 接收 I/O 请求,对其进行处理并完成

在最简单的方案中,KMDF 将请求调度到驱动程序,该驱动程序执行 I/O 并完成请求。 在这种情况下,基础缓冲区可能是由用户模式应用程序、其他驱动程序或操作系统本身创建的。

当驱动程序 完成请求时,框架将删除内存对象。 缓冲区指针随后无效。

方案 2:驱动程序从 KMDF 接收 I/O 请求并将其转发到 I/O 目标

在此方案中,驱动程序 将请求转发 到 I/O 目标。 以下示例代码演示驱动程序如何从传入请求对象检索内存对象的句柄、设置要发送到 I/O 目标的请求的格式,以及发送请求:

VOID
EvtIoRead(IN WDFQUEUE Queue,IN WDFREQUEST Request,IN size_t Length)
{NTSTATUS status;WDFMEMORY memory;WDFIOTARGET ioTarget;BOOLEAN ret;ioTarget = WdfDeviceGetIoTarget(WdfIoQueueGetDevice(Queue));status = WdfRequestRetrieveOutputMemory(Request, &memory);if (!NT_SUCCESS(status)) {goto End;}status = WdfIoTargetFormatRequestForRead(ioTarget,Request,memory,NULL,NULL);if (!NT_SUCCESS(status)) {goto End;}WdfRequestSetCompletionRoutine(Request,RequestCompletionRoutine,WDF_NO_CONTEXT);ret = WdfRequestSend (Request, ioTarget, WDF_NO_SEND_OPTIONS);if (!ret) {status = WdfRequestGetStatus (Request);goto End;}return;End:WdfRequestComplete(Request, status);return;}

当 I/O 目标完成请求时,框架将调用驱动程序为请求设置的完成回调。 以下代码显示了一个简单的完成回调:

VOID
RequestCompletionRoutine(IN WDFREQUEST                  Request,IN WDFIOTARGET                 Target,PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,IN WDFCONTEXT                  Context)
{UNREFERENCED_PARAMETER(Target);UNREFERENCED_PARAMETER(Context);WdfRequestComplete(Request, CompletionParams->IoStatus.Status);return;}

当驱动程序从其完成回调调用 WdfRequestComplete 时,框架将删除内存对象。 驱动程序检索的内存对象句柄现在无效。

方案 3:驱动程序发出使用现有内存对象的 I/O 请求。

某些驱动程序发出自己的 I/O 请求并将其发送到 I/O 目标,这些目标由 I/O 目标对象表示。 驱动程序可以创建自己的请求对象,也可以 重复使用框架创建的请求对象。 使用任一技术,驱动程序都可以重复使用来自上一个请求的内存对象。 驱动程序不得更改基础缓冲区,但在设置新 I/O 请求的格式时,它可以传递缓冲区偏移量。

当框架格式化要发送到 I/O 目标的请求时,它将代表 I/O 目标对象对回收的内存对象进行引用。 I/O 目标对象将保留此引用,直到执行以下操作之一:

  • 请求已完成;
  • 驱动程序通过调用 WdfIoTargetFormatRequestXxx 或 WdfIoTargetSendXxxSynchronously 方法之一再次重新设置请求对象的格式;
  • 驱动程序调用 WdfRequestReuse;

新的 I/O 请求完成后,框架将调用驱动程序为此请求设置的 I/O 完成回调。 此时,I/O 目标对象仍保留对内存对象的引用。 因此,在 I/O 完成回调中,驱动程序必须先对驱动程序创建的请求对象调用 WdfRequestReuse ,然后才能完成从中检索内存对象的原始请求。 如果驱动程序不调用 WdfRequestReuse,则由于额外的引用,检查会出现 bug。

方案 4:驱动程序发出使用新内存对象的 I/O 请求

框架为驱动程序提供了三种方法来创建新的内存对象,具体取决于基础缓冲区的源。 有关详细信息,请参阅 使用内存缓冲区。

如果缓冲区由框架分配或从驱动程序创建的 lookaside 列表中分配,则内存对象拥有该缓冲区,因此只要内存对象存在,缓冲区指针就保持有效。 发出异步 I/O 请求的驱动程序应始终使用内存对象拥有的缓冲区,以便框架可以确保缓冲区一直存在,直到 I/O 请求完成发证驱动程序。

如果驱动程序通过调用 WdfMemoryCreatePreallocated 将以前分配的缓冲区分配给新的内存对象,则内存对象不拥有该缓冲区。 在这种情况下,内存对象的生存期与基础缓冲区的生存期无关。 驱动程序必须管理缓冲区的生存期,并且不得尝试使用无效的缓冲区指针。

方案 5:驱动程序重复使用它创建的请求对象

驱动程序可以重用其创建的请求对象,但在每次重用之前,它必须通过调用 WdfRequestReuse 来重新初始化每个此类对象。

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

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

相关文章

[C++]使用C++部署yolov10目标检测的tensorrt模型支持图片视频推理windows测试通过

【测试通过环境】 vs2019 cmake3.24.3 cuda11.7.1cudnn8.8.0 tensorrt8.6.1.6 opencv4.8.0 【部署步骤】 获取pt模型:https://github.com/THU-MIG/yolov10训练自己的模型或者直接使用yolov10官方预训练模型 下载源码:https://github.com/laugh12321/yol…

波卡近期活动一览| Polkadot Decoded 2024 重磅来袭,300 万 DOT 将用于 DeFi 增长

Polkadot 生态近期活动精彩纷呈,线上线下火热进行中!此外,Polkadot 2.0 的关键升级即将到来,Gavin Wood 博士也将在最新访谈节目中分享更多关于波卡的未来发展蓝图。波卡 DAO 通过提案,分配 300 万 DOT 支持 DeFi 生态…

C++小游戏 合集2

给大家整理了一些c小游戏希望大家喜欢 第一个球球飞车 #include <bits/stdc.h> #include <stdio.h> #include <conio.h> #include <cstdlib> #include <windows.h> #include <iostream> #include <fstream> using namespace std;i…

全量知识系统 程序详细设计 再审: 三个层次及相应的编程语言特点

Q1.今天我们重新聊聊全量知识系统&#xff08;以下简称“全知系统”&#xff09;程序详细设计的三个层次及其编程语言的主要特点. A1.全量知识系统程序的详细设计通常涉及三个主要层次&#xff1a;数据层、业务逻辑层以及用户界面层。每个层次都有其特定的功能和编程语言的主要…

房地产房型展示信息小程序的内容是什么

地产业规模之大且品牌众多&#xff0c;还有房屋租赁、中介等&#xff0c;无论开发商公司还是衍生行业商家都需要多渠道宣传品牌和客户触达沟通转化&#xff0c;除了线下各种传单&#xff0c;线上也是主要场景&#xff0c;通过各种连接来达到相应目标。 也因此需符合平台生态开…

掌握JavaScript中的`async`和`await`:循环中的使用指南

引言 在JavaScript的异步编程中&#xff0c;async和await提供了一种更接近同步代码的写法&#xff0c;使得异步逻辑更加清晰易懂。然而&#xff0c;当它们与循环结合时&#xff0c;一些常见的陷阱和误区可能会出现。本文将通过代码示例&#xff0c;指导你如何在循环中正确使用…

读AI新生:破解人机共存密码笔记01以史为鉴

1. 科学突破是很难预测的 1.1. 20世纪初&#xff0c;也许没有哪位核物理学家比质子的发现者、“分裂原子的人”欧内斯特卢瑟福&#xff3b;Ernest Rutherford&#xff3d;更为杰出 1.1.1. 卢瑟福早就意识到原子核储存了巨大的能量&#xff0c;然而&#xff0c;主流观点认为开…

C语言 内存对齐

内存对齐 内存对齐作用 平台原因(移植原因)&#xff1a;不是所有的硬件平台都能访问任意地址上的任意数据的&#xff1b;某些硬件平台只能在某些地址处取某些特定类型的数据&#xff0c;否则抛出硬件异常。性能原因&#xff1a;数据结构(尤其是栈)应该尽可能地在自然边界上对…

Flink Watermark详解

Flink Watermark详解 一、概述 Flink Watermark是Apache Flink框架中为了处理乱序和延迟事件时间数据而引入的一种机制。在流处理中&#xff0c;由于数据可能不是按照事件产生的时间顺序到达的&#xff0c;Watermark被用来告知系统在该时间戳之前的数据已经全部到达&#xff…

Qt 6.13

作业&#xff1a; #include "mywidget.h"mywidget::mywidget(QWidget *parent): QWidget(parent) {this->setStyleSheet("background-color:white");this->resize(600,600);this->setWindowFlag(Qt::FramelessWindowHint);this->setWindowTit…

Web前端快速开发平台:革命性工具,提升开发效率的新篇章

Web前端快速开发平台&#xff1a;革命性工具&#xff0c;提升开发效率的新篇章 在数字化时代的浪潮中&#xff0c;Web前端技术的快速发展与变革正在重塑我们的数字世界。为了应对这种快速变化&#xff0c;Web前端快速开发平台应运而生&#xff0c;为开发者们提供了更加高效、便…

Opencv数一数有多少个水晶贴纸?

1.目标-数出有多少个贴纸 好久没更新博客了&#xff0c;最近家里小朋友在一张A3纸上贴了很多水晶贴纸&#xff0c;要让我帮他数有多少个&#xff0c;看上去有点多&#xff0c;贴的也比较随意&#xff0c;于是想着使用Opencv来识别一下有多少个。 原图如下&#xff1a; 代码…

centos7系统使用docker-compose安装部署jenkins

CentOS7系统使用docker-compose安装部署jenkins&#xff0c;并实现前后端自动构建 记录一次在给公司部署jenkins的真实经历&#xff0c;总结了相关经验 1.准备环境 1.java 由于最新的jenkins需要jdk11以上才能支持&#xff0c;而系统里的jdk是1.8的&#xff0c;因此等jenkins…

vue项目问题汇总

1.el-select&#xff1a; 下拉框显示到了top:-2183px , 添加属性 :popper-append-to-body"false" 2. el-upload: 选过的文件在使用过后记得清空&#xff0c;因为如果有limit1的时候&#xff0c;没有清空会导致不触发onchange 使用自定义上传方法http-request的时…

Swift开发——输出格式化字符

Swift语言是开发iOS和macOS等Apple计算机和移动设备系统应用程序的官方语言。Swift语言是一种类型安全的语言,语法优美自然,其程序从main.swift文件开始执行,程序代码按先后顺序执行,同一个工程的程序文件中的类和函数直接被main.swift文件调用,除了main.swift文件外,工程…

工业自动化领域常见的通讯协议

工业自动化领域常见的通讯协议&#xff0c;包括PROFINET、PROFIBUS、Modbus、Ethernet/IP、CANopen、DeviceNet和BACnet。通过分析这些协议的技术特点、应用场景及优势&#xff0c;比较它们在工业自动化中的性能和适用性&#xff0c;帮助选择最合适的协议以优化系统性能和可靠性…

利用Axios封装及泛型实现定制化HTTP请求处理

本案例旨在教授如何使用Axios库结合TypeScript泛型进行HTTP请求的高级封装&#xff0c;以提升代码的可复用性和类型安全性。我们将通过一个具体的示例&#xff0c;学习如何创建一个通用的请求函数&#xff0c;它能够适应不同类型的API响应&#xff0c;并在请求前后加入自定义逻…

跨文化美学实践:以‘Shockman登峰侠‘为例探析翻译艺术与文化意蕴

"Shockman登峰侠"这一组合&#xff0c;实际上是在尝试融合直译与意译的翻译策略&#xff0c;既保留了原英文名称&#xff0c;又通过附加一个富有象征意义的中文称号来丰富角色的形象和内涵。这种翻译实践&#xff0c;展现了汉语翻译美学中的几个重要方面&#xff1a;…

LeetCode 每日一题 2024/6/10-2024/6/16

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 6/10 881. 救生艇6/11 419. 甲板上的战舰6/12 2806. 取整购买后的账户余额6/13 2813. 子序列最大优雅度6/14 2786. 访问数组中的位置使分数最大6/15 2779. 数组的最大美丽值…

React@16.x(26)useContext

目录 1&#xff0c;上下文的使用2&#xff0c;useContext 1&#xff0c;上下文的使用 之前的文章中介绍过 context上下文。 使用举例&#xff1a; import React, { useState } from "react";const ctx React.createContext();function Child() {return <ctx.C…