[转&精]IO_STACK_LOCATION与IRP的一点笔记

2023-05-16

IO_STACK_LOCATION和IRP算是驱动中两个很基础的东西,为了理解这两个东西,找了一点资料。

1. IRP可以看成是Win32窗口程序中的消息(Message),DEVICE_OBJECT可以看成是Win32窗口程序中的窗口(Window)

2. 任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序。
IRP的头部有一个当前IO_STACK_LOCATION的数组索引,同时也有一个指向该IO_STACK_LOCATION的指针。索引是从1开始, 没有0。当驱动程序准备向次低层驱动程序传递IRP时可以调用IoCallDriver例程,它其中的一个工作是递减当前 IO_STACK_LOCATION的索引,使之与下一层的驱动程序匹配。但该索引不会设置成0,如果设置成0,系统将会崩溃。就是说,最底层的驱动程序 不会调用IoCallDriver例程。

3. IO_STACK_LOCATION中有一个PIO_COMPLETION_ROUTINE类型的成员CompletionRoutine,这是一个I/O完成例程的地址,该地址是由与这个堆栈单元对应的驱动程序的更上一层驱动程序设置的。你绝对不要直接设置这个域,应该调用IoSetCompletionRoutine函数,该函数知道如何参考下一层驱动程序的堆栈单元。设备堆栈的最低一级驱动程序并不需要完成例程,因为它们必须直接完成请求。然而,请求的发起者有时确实需要一个完成例程,但通常没有自己的堆栈单元。这就是为什么每一级驱动程序都使用下一级驱动程序的堆栈单元保存自己完成例程指针的原因。


 1 VOID
 2 IoSetCompletionRoutine(
 3     __in PIRP Irp,
 4     __in_opt PIO_COMPLETION_ROUTINE CompletionRoutine,
 5     __in_opt __drv_aliasesMem PVOID Context,
 6     __in BOOLEAN InvokeOnSuccess,
 7     __in BOOLEAN InvokeOnError,
 8     __in BOOLEAN InvokeOnCancel
 9     )
10 {
11     PIO_STACK_LOCATION irpSp;
12     ASSERT( (InvokeOnSuccess || InvokeOnError || InvokeOnCancel) ? (CompletionRoutine != NULL) : TRUE );
13     irpSp = IoGetNextIrpStackLocation(Irp);
14     irpSp->CompletionRoutine = CompletionRoutine;
15     irpSp->Context = Context;
16     irpSp->Control = 0;
17  
18     if (InvokeOnSuccess) {
19         irpSp->Control = SL_INVOKE_ON_SUCCESS;
20     }
21  
22     if (InvokeOnError) {
23         irpSp->Control |= SL_INVOKE_ON_ERROR;
24     }
25  
26     if (InvokeOnCancel) {
27         irpSp->Control |= SL_INVOKE_ON_CANCEL;
28     }
29 }  
 

总算解释了一下为什么IoSetCompletionRoutine中把完成例程设置在下一个IO堆栈之中。最底层的驱动程序不应该安装一个完成例程。

4. 完成例程的框架


 1 NTSTATUS CompletionRoutine(PDEVICE_OBJECT device, PIRP Irp, PVOID context)
 2 {
 3     if (Irp->PendingReturned)
 4     {
 5         IoMarkIrpPending(Irp);
 6     }
 7     // ...
 8  
 9     return STATUS_SUCCESS /* or some other status code */ ;
10 }  
 

如果Irp->PendingReturned为TRUE,那么任何不返回STATUS_MORE_PROCESSING_REQUIRED的完成例程都应该调用IoMarkIrpPending,这几乎完全是对的,但仍有例外。如果驱动程序分配了IRP,安装了完成例程,然后在未改变堆栈指针的情况下调用IoCallDriver,那么完成例程就不应该包含这两行代码,因为没有堆栈单元与你的驱动程序关联。(下划线部分理解不太清楚,可以先不做理解,实际情况注意下就是了。有些书甚至都没有提及这一点)

5. 如果你的驱动程序不用关心IRP传递到下层驱动程序之后的事情,没有必要花费处理器时间(调用 IoCopyCurrentIrpStackLocationToNext)去把你的堆栈单元内容复制到下一个堆栈单元,因为那个堆栈单元已经含有下一层 驱动程序要得到的参数,以及自己上一层驱动程序可能给出的任何完成例程指针。因此可以用下面的代码:


1 NTSTATUS ForwardAndForget(PDEVICE_OBJECT fdo, PIRP Irp)
2 {
3     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
4     IoSkipCurrentIrpStackLocation(Irp);
5     return IoCallDriver(pdx->LowerDeviceObject, Irp);
6 }  
 

这样的代码是不是在过滤驱动中经常见到呢?

6. IoCopyCurrentIrpStackLocationToNext和IoSkipCurrentIrpStackLocation
IoCopyCurrentIrpStackLocationToNext复制IO堆栈除了完成例程以及完成例程参数以为的内容。


 1 VOID
 2 IoCopyCurrentIrpStackLocationToNext(
 3     __inout PIRP Irp
 4 )
 5 {
 6     PIO_STACK_LOCATION irpSp;
 7     PIO_STACK_LOCATION nextIrpSp;
 8     irpSp = IoGetCurrentIrpStackLocation(Irp);
 9     nextIrpSp = IoGetNextIrpStackLocation(Irp);
10     RtlCopyMemory( nextIrpSp, irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));
11     nextIrpSp->Control = 0;
12 }  

 

 

IoSkipCurrentIrpStackLocation使堆栈指针少前进一步,而IoCallDriver函数会使堆栈指针向前一步,中和的 结果就是堆栈指针不变。当下一个驱动程序的派遣例程调用IoGetCurrentIrpStackLocation时,它将收到与我们正使用的完全相同的 IO_STACK_LOCATION指针。


1 VOID
2 IoSkipCurrentIrpStackLocation (
3     __inout PIRP Irp
4 )
5 {
6     ASSERT(Irp->CurrentLocation <= Irp->StackCount);
7     Irp->CurrentLocation++;
8     Irp->Tail.Overlay.CurrentStackLocation++;
9 }  
 

原来我在这里是有疑问的:一个IRP对应一个IO_STACK_LOCATION,如果使用IoSkipCurrentIrpStackLocation那不是少了一个IO_STACK_LOCATION吗?是的,确实是这样,书上都没有说清楚~~看下面的图吧:
图中显示了这样一种情形:某设备堆栈有三个驱动程序,你的驱动程序(功能设备对象[FDO])和其它两个驱动程序(一个上层过滤器设备对象[FiDO], 一个PDO)。在图(a)中,你将看到执行复制堆栈单元的IoCopyCurrentIrpStackLocationToNext函数,堆栈单元、各个 参数,和完成例程之间的关系。在图(b)中,你将看到还是这样的关系,但使用的是IoSkipCurrentIrpStackLocation函数,第三 个和最后一个堆栈单元被跳过。【注:原文中的"第三 个和最后一个堆栈单元被跳过。"(the third and last stack location is fallow, but nobody gets confused by that fact.)的第三个和最后一个应该说的是同一个堆栈单元。】

7. IRP完成例程与STATUS_MORE_PROCESSING_REQUIRED
如果某一层的驱动希望下发的IRP在完成之时能够再次获得IRP的控制权,那么可以再完成例程中返回STATUS_MORE_PROCESSING_REQUIRED。代码看起来如下:


 1 NTSTATUS ForwardAndWait(PDEVICE_OBJECT fdo, PIRP Irp)
 2 {
 3     KEVENT event;
 4     KeInitializeEvent(&event, NotificationEvent, FALSE);
 5     IoCopyCurrentIrpStackLocationToNext(Irp);
 6     IoSetCompletionRoutine(Irp,
 7             (PIO_COMPLETION_ROUTINE) OnRequestComplete,
 8             (PVOID) &event,
 9             TRUE,
10             TRUE,
11             TRUE);
12     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
13     IoCallDriver(pdx->LowerDeviceObject, Irp);
14     KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
15     return Irp->IoStatus.Status;
16 }
17  
18 NTSTATUS OnRequestComplete(PDEVICE_OBJECT fdo, PIRP Irp, PKEVENT pev)
19 {
20     KeSetEvent(pev, 0, FALSE);
21     return STATUS_MORE_PROCESSING_REQUIRED;
22 }  
 

一旦我们调用了IoCallDriver,我们就放弃了IRP的控制权,直到某些运行在任意线程上下文中的代码调用 IoCompleteRequest通知该IRP完成,IoCompleteRequest将调用我们的完成例程。通过在完成例程中返回 STATUS_MORE_PROCESSING_REQUIRED,我们停止了I/O堆栈的回卷处理。此时,上层过滤器驱动程序安装的任何完成例程都得不 到调用,并且I/O管理器将停止在该IRP上的工作。这种情形就象根本没有调用过IoCompleteRequest一样,当然,某些已经调用过的低级完 成例程除外。在这一时刻,该IRP将处于一个中间状态,但我们的ForwardAndWait例程将再次获得该IRP的所有权。

为什么要重新控制IRP呢?有一种情况是:这个IRP是当前的驱动动态分配的,转发给下层驱动之后,这个IRP总要在下面的驱动处理完之后把IRP分配用的空间回收吧?

Reference:《Programming the Microsoft Windows Driver Model》


原文地址: 程序人生 >> IO_STACK_LOCATION与IRP的一点笔记
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!

转载于:https://www.cnblogs.com/csstudy/p/11603512.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[转&精]IO_STACK_LOCATION与IRP的一点笔记 的相关文章

  • html 堆栈顺序

    考虑以下代码
  • 缓冲区溢出攻击(攻击实验室第 2 阶段)

    我有一个缓冲区溢出实验室 我必须为一个名为攻击实验室 http csapp cs cmu edu 3e attacklab pdf 我处于实验室的第二阶段 我必须将代码作为漏洞利用字符串的一部分注入 以使程序指向函数 touch2 的地址
  • Android位置请求PRIORITY_HIGH_ACCURACY没有效果

    我的 Android 应用程序需要高精度位置跟踪 应用程序启动时 它会以编程方式读取位置设置 并在以下情况下显示一个屏幕 高准确率未选择 我改编了Google官方的例子 https developers google com android
  • 虽然places.getLatLng()返回null,但places.getName()不返回null

    我一直在尝试从自动完成中单击一个位置后获取经纬度 奇怪的是places getName 工作正常但是place getLatLng 返回空值 我应该怎么做才能解决这个问题我是谷歌地图和地点 API 的新手 protected void on
  • 如何将 JFrame 位置设置在另一个 JFrame 旁边?

    在 Java 中 如何设置 JFrame 自动位于另一个 JFrame 旁边 所以 假设我有两个JFrame物体 frameA and frameB 当程序运行时 它设置frameA使用以下命令将 s 位置设置在屏幕中间 setLocati
  • 在 D 中制作结构体的堆副本

    如何创建堆栈上结构的垃圾收集副本 来自 C 背景 我的第一个猜测是像下面这样的复制构造函数 但它对于 D 来说似乎不太惯用 而且我在我看过的任何 D 项目中都没有看到过这样的复制构造函数 struct Foo immutable int b
  • 堆栈在缓存中吗?

    在现代计算机中 我知道当前代码区域位于高速缓存中 然而 在许多计算机语言实现中 本地 自动 变量将位于堆栈上 因此会对堆栈进行大量内存访问 在正常架构中 堆栈是否位于另一个缓存中 如果不是 则假设堆栈可以重新定位到 本地 即非常靠近当前代码
  • 什么是 C/C++ 数据段和堆栈大小?

    我读到这取决于编译器和操作系统架构 如何在使用 GCC 作为编译器的 Linux 系统上找到数据段和堆栈最大大小 让我和你一起实验一下 创建文件 test c 如下所示 int main void return 0 现在编译它 指定最大堆栈
  • 如何检测访问者的国家/地区? [复制]

    这个问题在这里已经有答案了 可能的重复 IP地址的位置检测技术 https stackoverflow com questions 2574542 location detecting techniques for ip addresses
  • 为什么在函数堆栈上返回值不安全

    我在阅读 bruce eckel 时遇到了以下段落 他试图解释为什么函数在堆栈上返回值不安全 现在想象一下如果一个普通函数尝试在堆栈上返回值会发生什么您不能触及返回地址上方堆栈的任何部分 因此该函数必须将值推入返回地址下方 但是当执行汇编语
  • 使用callstack在C中实现堆栈数据结构?

    我对 C 下内存结构的理解是 程序的内存与堆栈和堆分开 每个堆栈和堆都从块的两端生长 可以想象分配所有 RAM 但显然抽象为某种操作系统内存片段管理器 堆栈设计用于处理局部变量 自动存储 堆设计用于内存分配 动态存储 编者注 有一些 C 实
  • 从 Android 应用程序堆栈中手动删除活动

    我一直在开发 Android Native App 我想做的是 Activities A gt B gt C Then A gt B gt C gt C 从 C Activity 中 如果它再次指向 C 那么我想手动从堆栈中删除 C B 在
  • 如何重定向到移动网站但保留主题标签?

    我希望拥有它 以便当用户加载我们的页面时 它会检查他们是否在移动设备上 然后将他们重定向到我们网站的移动版本 但保留原始哈希标签他们关注的链接 我尝试使用 javascript 中的哈希标签设置新位置 它在 Chrome 中工作 但在 Sa
  • Android 中如何在不使用 getLastKnownLocation 方法的情况下获取当前的纬度和经度?

    我正在尝试获取current手机的位置 为此我使用GPS追踪器教程 http www androidhive info 2012 07 android gps location manager tutorial 问题总是使用该方法getLa
  • std::stack 是否公开迭代器?

    是否std stack在 C STL 中公开底层容器的任何迭代器 还是应该直接使用该容器 根据堆栈的定义 堆栈没有迭代器 如果您需要带有迭代器的堆栈 则需要自己在其他容器 std list std vector 等 之上实现它 堆栈文档在这
  • 如何增加 Qt 中线程的堆栈大小 - QThread::setStackSize() 似乎不起作用?

    从问题来看 运行批量插入或替换 500 行时 SQLite 堆栈溢出 为什么 https stackoverflow com questions 22576958 sqlite stack overflow when running a b
  • Android Locationprovider 需要太长时间才能暂时不可用

    根据这篇文章 UPD 链接已删除 因为它现在会导致一些废话 设置minTime当请求位置更新时将导致提供程序将自身设置为 TEMPORARILY UNAVAILABLEminTime毫秒以节省电池电量 在此不活动期间 GPS 提供商将自行关
  • 请求单个位置更新,AVD 崩溃

    我尝试使用新的 LocationManager requestSingleUpdate 方法请求单个位置更新 但是一旦设备从 GPS 获取更新 操作系统就会崩溃 并且似乎会尝试重新启动 至少我看到了通常的 Android 启动屏幕 但它永远
  • Android:计算两个位置之间距离的最佳方法

    我在这个主题上做了一些研究 但有很多观点并没有给出一个清晰的图像 我的问题是这样的 我正在为 Android 开发一个基于 GPS 的应用程序 在其中我想实时了解 Android LocationManager 指定的当前位置与其他位置之间
  • printf() var-arg 引用如何与堆栈内存布局交互?

    给出代码片段 int main printf Val d 5 return 0 是否有任何保证编译器会存储 Val d and 5 连续地 例如 d l a V 5 Format String

随机推荐

  • arduino 语音音箱 :语音控制、MP3播放、报时、回复温湿度情况

    arduino 语音音箱 xff1a 语音控制 MP3播放 报时 回复温湿度情况 效果图 线路图 包装后的效果 功能 需要材料 arduino板MP3播放模块及喇叭时钟模块温湿度模块语音识别模块面包板及其他线材 电阻TF卡 xff08 用于
  • 通过多张网卡发送UDP多播(组播)数据

    在具有多个网卡的机器上 xff0c 如果想要从每个网卡发送UDP数据 xff0c 一般的做法是 xff1a 针对每张网卡的每个IP都绑定一个SOCKET xff0c 然后发送的时候针对每个SOCKET都发送一次 但是如果你要发送多播数据 x
  • 常见的HTTP状态码

    本内容摘抄自RESTful WebServices 中文译本附录B 39 42种常见的HTTP响应代码 39 原文作者 xff1a Leonard Ricbardson amp Sam Ruby 翻译 xff1a 徐涵 李红军 胡伟 1 三
  • ps快速切图

    妈呀 xff0c 不得不感慨一下 xff0c 切了这么久的图 xff0c 竟然不知道有个切图工具这么好用 以前我的切图流程 xff1a 拿到ui设计好的psd文件 61 61 gt 拉基准线 61 61 gt 切片工具切图 61 61 gt
  • MongoDB v4.0 命令

    MongoDB v4 0 命令 官方文档 gt 点这里 lt 操作系统库 操作管理员库 use admin 鉴权 db auth 34 root 34 34 admin 34 用户查看 格式美化 db system users find p
  • 查看端口是否可访问(防火墙拦截处理)

    telnet ip 端口 例如 xff1a telnet 10 20 113 15 8080 出现 Escape character is 表示连接 xff0c 没有被防火墙拦截 转载于 https www cnblogs com kdx
  • ESP32-CAM上手

    硬件 ESP32 CAM摄像头开发板 安信可科技 ESP32 CAM摄像头开发板 https item taobao com item htm spm 61 a1z09 2 0 0 67002e8dvbTVMF amp id 61 6159
  • 面试时如何做好5分钟自我介绍?

    有简历 xff0c 为何还要自我介绍 xff1f 一个常规的面试 xff0c 寒暄之后面试官提出的第一个问题几乎千篇一律 xff1a 请您简单地做一下自我介绍 有些被面试者都会问 xff1a 简历中情况已经写得很清楚了 xff0c 这是否多
  • windows查看当前python的版本

    1 Ctrl 43 R打开控制台 输入python之后回车 转载于 https www cnblogs com CK85 p 10243904 html
  • 利用MATLAB绘制置信区域

    lt MATLAB小技巧之二十四 xff1a 利用MATLAB绘制置信区域 gt 统计中经常会遇到求置信区间 置信区域 xff08 如置信椭圆 置信椭球 xff09 等 xff0c 有时候需要把置信区域画出来 xff0c 这样看起看更为直观
  • 关于latex编译中文不显示问题的解决方法。

    我的编译环境是texlive2018 43 texstudio 配置如图 默认编码格式为utf8 直接上代码 documentclass article usepackage xeCJK documentclass UTF8 ctexart
  • 最新百度 阿里 华为 腾讯 谷歌面试笔试题及解析 (转)

    原文地址 xff1a http m blog csdn net blog panfengyun12345 12618453 8月15日 xff0c 百度2道面试题 xff1a 1 来自 编程之美 的概率题 xff1a 一个桶里面有白球 黑球
  • 归并排序——C语言

    归并排序 归并排序 xff08 MERGE SORT xff09 是建立在归并操作上的一种有效的排序算法 该算法采用经典的分治 xff08 divide and conquer xff09 策略 xff08 分治法将问题分 divide 成
  • Linux设备驱动程序学习(19)-USB 驱动程序(四)

    编写 USB 驱动程序 xff08 本部分的一些示例源码来自drivers usb usb skeleton c xff0c 它是Linux内核为我们提供的最基础的USB驱动程序 xff0c USB骨架程序 xff09 驱动程序把驱动对象注
  • Linux----面试

    1 tcp和udp的区别 TCP xff1a 是面向连接的流传输控制协议 xff0c 具有高可靠性 xff0c 确保传输数据的正确性 xff0c 有验证重发机制 xff0c 因此不会出现丢失或乱序 UDP xff1a 是无连接的数据报服务
  • ssh免密码登陆失败的原因

    今天因为需要在两台服务器上进行ssh免登陆 xff0c 所以安装网上的教程 xff0c ssh keygen t rsa xff0c 然后把相互的密钥加入到对方的authorized keys 问题是我们虽然这样做了 xff0c 却一直要密
  • ESP32-s2芯片esp32-s2-saola-1开发板 micropython的repl连接

    本文只是解决通过esp32 s2 saola 1开发板 自带microUSB 作为repl与micro python通信的问题 如果你对esp32 xff0c micropython不熟 xff0c 本文不适合你 如果你用的不是esp32
  • 机械臂模拟2.0

    机械臂模拟 void MobileCrane updateHopeLength int center x int center y int center z int armNodeNum int ropePitchNum int baseN
  • Qt获取时间戳作为图片名

    Qt获取时间戳作为图片名 保存图片 void SaveRealsenseImg QString picIndexName 61 dataSavePath picIndexName append 34 34 获取当前时间 QDateTime
  • [转&精]IO_STACK_LOCATION与IRP的一点笔记

    IO STACK LOCATION和IRP算是驱动中两个很基础的东西 xff0c 为了理解这两个东西 xff0c 找了一点资料 1 IRP可以看成是Win32窗口程序中的消息 xff08 Message xff09 xff0c DEVICE