[转&精]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的一点笔记 的相关文章

  • Android 中如何在不使用 getLastKnownLocation 方法的情况下获取当前的纬度和经度?

    我正在尝试获取current手机的位置 为此我使用GPS追踪器教程 http www androidhive info 2012 07 android gps location manager tutorial 问题总是使用该方法getLa
  • Android 应用无法获取位置权限

    我的清单中有以下内容
  • 在不改变我的位置的情况下获取当前位置的经度和纬度

    我可以找到当前位置的纬度和经度 但是这些数据在更改我的当前位置之前不会显示 我想在不更改我的位置的情况下获取当前位置的经度和纬度 package com example gps import android app Activity imp
  • 如何增加 Qt 中线程的堆栈大小 - QThread::setStackSize() 似乎不起作用?

    从问题来看 运行批量插入或替换 500 行时 SQLite 堆栈溢出 为什么 https stackoverflow com questions 22576958 sqlite stack overflow when running a b
  • C 函数堆栈布局

    我有一个看起来像这样的函数 int bof char str char buffer 12 strcpy buffer str return 1 我正在尝试覆盖其返回地址 我发现我可以通过使用来做到这一点 例如 memcpy buffer
  • 如何在 Android 中像 Google 地图一样获得持续的位置更新?

    我正在构建一个朋友跟踪 Android 应用程序 当我的朋友激活应用程序并带着他的 GPS 和蜂窝数据离开时 我需要在我的设备上跟踪他 这就是这个概念 我已经实现了 LocationListener 类 现在我可以从 Gps 或网络获取最后
  • 不要在异常堆栈中显示 Python raise-line

    当我在 Python 库中引发自己的异常时 异常堆栈将引发行本身显示为堆栈的最后一项 这显然不是一个错误 在概念上是正确的 但是当您在外部使用代码 例如作为模块 时 它会将重点放在对调试无用的东西上 有没有办法避免这种情况并强制 Pytho
  • 有没有比使用 backtrace() 更便宜的方法来查找调用堆栈的深度?

    我的日志记录代码使用的返回值回溯 http linux die net man 3 backtrace确定当前堆栈深度 出于漂亮的打印目的 但我可以从分析中看到这是一个相当昂贵的调用 我不认为有更便宜的方法吗 请注意 我不关心帧地址 只关心
  • C - '=' 标记之前的预期表达式...在没有 '=' 的行上

    我疯狂地试图找出这个与现实 我的代码没有明显联系的错误消息 我一直在这里搜索并得出一个结论 你会讨厌 typedef 隐藏的指针 抱歉 这超出了我的控制范围 教授以这种方式提供了代码 我正在编辑问题中指定的代码 我弹出完整节点以避免每个推送
  • 访问 Linux 线程(pthreads)的本地堆栈

    我目前正在实现一个使用多线程但对总内存消耗有要求的应用程序 我希望有一个主线程执行 I O 并有几个工作线程执行计算 目前 我在主堆栈上有几个可供工作人员访问的数据结构 我使用 OpenMP 进行工作分配 由于主 工作者模式不能很好地与 O
  • 如何使用延迟位置 iOS 6?

    我正在尝试使用新的 iOS 6 延迟位置更新功能 但不断收到此错误 didFinishDeferredUpdatesWithError Error Domain kCLErrorDomain Code 11 操作无法完成 kCLErrorD
  • 将 DIV 堆叠在一起?

    是否可以堆叠多个 DIV 例如 div div div div div div div div div div 那么所有这些内部 DIV 都具有相同的 X 和 Y 位置吗 默认情况下 它们都在彼此下方 将 Y 位置增加了上一个 DIV 的高
  • Android 上下文不在活动中?还有其他无活动编程吗?

    我会非常努力地将其变成一个综合问题 我正在编写一个方法来获取一个包含 Android 设备城市名称的字符串 该名称由LocationManager and getLastKnownLocation 等等 然后我意识到我需要在另一个活动中再次
  • 了解C/C++中函数调用的堆栈框架? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我也是 C C 和汇编语言的新手 这
  • 检测到堆栈崩溃

    我正在执行我的 a out 文件 执行后 程序运行一段时间 然后退出并显示消息 stack smashing detected a out terminated Backtrace lib tls i686 cmov libc so 6 f
  • Android Marshmallow 应用程序中位置的权限问题

    我正在学习开发一个 Android 应用程序 用于按照 Google 开发者论坛获取设备位置 http developer android com training location retrieve current html last k
  • 如何在Python中另一个类的函数中获取调用者类名?

    我的目标是模拟应用程序的序列图 为此我需要有关运行时调用者和被调用者类名的信息 我可以成功检索调用者函数 但无法获取调用者类名 Scenario caller py import inspect class A def Apple self
  • Nginx 位置、别名、重写、根

    我正在通过 proxypass 提供 foo bar 服务 并希望继续这样做 但是 我想从 var www mystatic baz swf 等静态地提供 foo bar baz swf 服务 我希望我能做类似的事情 location fo
  • 在android中快速获取当前位置

    我有一个 Android 应用程序需要设备当前位置 纬度和经度 我已经尝试了网上的一些教程 特别是堆栈溢出的一些解决方案 但它们对我来说效果不佳 我的要求非常简单 首先 我需要它速度快 并且在片段开始时需要一次位置 其次 我需要它尽可能精确
  • 如何打开定位服务

    当有人第一次拒绝时 如何从实际应用程序重新打开定位服务 我可以选择关闭或打开它 您只能提示他们在屏幕上打开定位服务 如下所示 UIApplication sharedApplication openURL NSURL URLWithStri

随机推荐

  • 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