构建自己的IRP来处理I/O
2008-04-18 09:26
274 查看
在做NT 驱动时,我们在读写时经常会遇到:我们只有FILE_OBJECT可用,但是Zw***例程却要求HANDLE;或者我们通过ZwCreateFile返 回的HANDLE却无法在后面使用。为了解决此问题,我们只有自己构建IRP了。(注:FILE_OBJECT在任何上下文环境中都是有效的,而 HANDLE只有在当前进程中才有效)。
申请(Allocation)
最简单和常用的方法是调用IoAllocateIrp,I/O manager会申请一个拥有合适数量stack locations的IRP。(注意:通过IoAllocateIrp申请的IRP不能再调用IoInitializeIrp,否则会破坏I/O manager设定的标志信息)。
在NT启动时,I/O manager会创建两个look-aside链表,分别对应于单个stack location和四个stack locations。如果你需要多余四个stack locations的IRP,为了提高性能,你可以创建自己的IRP池,并用链表来管理你的IRPs;你在使用时从链表中获取,用完之后返回给链表。为了 使I/O manager在IRP完成后返回给你,你需要注册完成例程(IoSetCompetionRoutine)。
如果你自己维护IRPs,可以通过如下方法:
USHORT nIrpSize = IoSizeOfIrp(NumberOfStackLocation); // DeviceObject.StackSize
pIrp = ExAllocatePool(NonPagedPool, nIrpSize);
if( NULL == pIrp) ....;
IoInitializeIrp(pIrp, nIrpSize, NumberOfStackLocation);
构建(Building)
申请了IRP后,我们必须做必要的填充,以使底层知道你的请求。需要填充的IRP域
MdlAddress
MdlAddress、AssicatedIr.SystemBufer、UserBuffer根据需要填充某一个。
Flags
any appropriate flags (c.f., ntddk.h for the IRP_ flags)
AssociatedIrp.SystemBuffer
any data buffer for this I/O request
RequestorMode
UserMode or KernelMode. Typically, this is UserMode if the arguments being passed should be validated, KernelMode otherwise.
UserBuffer
any data buffer for this I/O request
Tail.Overlay.Thread
the PETHREAD for the original requestor
和Stack Location(IoGetNextIrpStackLocation)
MajorFunction
the function code for the I/O to be performed
MinorFunction
a minor function code for the I/O. This field should be zero if there is no minor function code.
Flags
any flags needed to modify the behavior of the I/O operation (c.f., ntddk.h for the SL_* flags.)
DeviceObject
the device to which your driver will pass the IRP.
FileObject
the file object representing the file for this I/O operation. Note that this is only used when sending IRPs to a file system.
完成(Completion)
对于自己申请的Irp,我们需要在完成例程中返回STATUS_MORE_PROCESSING_REQUIRED来阻止I/Omanager对此Irp的继续处理。然后,我们释放空间,或把Irp连回到我们的链表中。
当I/O manager执行一个I/O操作时,与此操作相关的IRP会被连接到线程的一个链表中,用于在线程退出时是否此IRP。因此,如果你自己去是否IRP,一定要保证从此IRP在线程链表不再存在。
什么时候不需要完成例程呢?当我们不关心I/O操作完成情况;你不释需要放Irp时。
注:完成例程可能与你的I/O操作线程的上下文环境不同,因此句柄和用户地址可能无效;完成例程不一定在PASSIVE_LEVEL上,可能在DISPATCH_LEVEL上,因此,你可能需要在完成例程中创建一个工作线程来保证一个操作的安全执行。
捷径
我们可以通过以下三个函数,快速的构建一个IRP。
* IoBuildAsynchronousFsdRequest(...)
* IoBuildSynchronousFsdRequest(...)
* IoBuildDeviceIoControlRequest(...)
后两个函数,会把构建的IRP连接到线程中,因此我们不要自己去释放他们。
前两个函数只能用于IRP_MJ_READ, IRP_MJ_WITE, IRP_MJ_FLUSH_BUFFER, IRP_MJ_SHUTDOWN;第三个函数只能用于IRP_MJ_DEVICE_CONTROL和 IRP_MJ_INTERNAL_DEVICE_CONTROL。
申请(Allocation)
最简单和常用的方法是调用IoAllocateIrp,I/O manager会申请一个拥有合适数量stack locations的IRP。(注意:通过IoAllocateIrp申请的IRP不能再调用IoInitializeIrp,否则会破坏I/O manager设定的标志信息)。
在NT启动时,I/O manager会创建两个look-aside链表,分别对应于单个stack location和四个stack locations。如果你需要多余四个stack locations的IRP,为了提高性能,你可以创建自己的IRP池,并用链表来管理你的IRPs;你在使用时从链表中获取,用完之后返回给链表。为了 使I/O manager在IRP完成后返回给你,你需要注册完成例程(IoSetCompetionRoutine)。
如果你自己维护IRPs,可以通过如下方法:
USHORT nIrpSize = IoSizeOfIrp(NumberOfStackLocation); // DeviceObject.StackSize
pIrp = ExAllocatePool(NonPagedPool, nIrpSize);
if( NULL == pIrp) ....;
IoInitializeIrp(pIrp, nIrpSize, NumberOfStackLocation);
构建(Building)
申请了IRP后,我们必须做必要的填充,以使底层知道你的请求。需要填充的IRP域
MdlAddress
MdlAddress、AssicatedIr.SystemBufer、UserBuffer根据需要填充某一个。
Flags
any appropriate flags (c.f., ntddk.h for the IRP_ flags)
AssociatedIrp.SystemBuffer
any data buffer for this I/O request
RequestorMode
UserMode or KernelMode. Typically, this is UserMode if the arguments being passed should be validated, KernelMode otherwise.
UserBuffer
any data buffer for this I/O request
Tail.Overlay.Thread
the PETHREAD for the original requestor
和Stack Location(IoGetNextIrpStackLocation)
MajorFunction
the function code for the I/O to be performed
MinorFunction
a minor function code for the I/O. This field should be zero if there is no minor function code.
Flags
any flags needed to modify the behavior of the I/O operation (c.f., ntddk.h for the SL_* flags.)
DeviceObject
the device to which your driver will pass the IRP.
FileObject
the file object representing the file for this I/O operation. Note that this is only used when sending IRPs to a file system.
完成(Completion)
对于自己申请的Irp,我们需要在完成例程中返回STATUS_MORE_PROCESSING_REQUIRED来阻止I/Omanager对此Irp的继续处理。然后,我们释放空间,或把Irp连回到我们的链表中。
当I/O manager执行一个I/O操作时,与此操作相关的IRP会被连接到线程的一个链表中,用于在线程退出时是否此IRP。因此,如果你自己去是否IRP,一定要保证从此IRP在线程链表不再存在。
什么时候不需要完成例程呢?当我们不关心I/O操作完成情况;你不释需要放Irp时。
注:完成例程可能与你的I/O操作线程的上下文环境不同,因此句柄和用户地址可能无效;完成例程不一定在PASSIVE_LEVEL上,可能在DISPATCH_LEVEL上,因此,你可能需要在完成例程中创建一个工作线程来保证一个操作的安全执行。
捷径
我们可以通过以下三个函数,快速的构建一个IRP。
* IoBuildAsynchronousFsdRequest(...)
* IoBuildSynchronousFsdRequest(...)
* IoBuildDeviceIoControlRequest(...)
后两个函数,会把构建的IRP连接到线程中,因此我们不要自己去释放他们。
前两个函数只能用于IRP_MJ_READ, IRP_MJ_WITE, IRP_MJ_FLUSH_BUFFER, IRP_MJ_SHUTDOWN;第三个函数只能用于IRP_MJ_DEVICE_CONTROL和 IRP_MJ_INTERNAL_DEVICE_CONTROL。
相关文章推荐
- 数据库事务(三):构建自己的TransactionManager实现事务处理
- 自己标注(不注意坑不少)-Spark+Kafka构建实时分析Dashboard案例——步骤三:Spark Streaming实时处理数据
- 对自己设备的控制IRP的处理
- JS--构建自己的函数库之——浏览器兼容问题处理(持续构建中……)
- Step by Step-构建自己的ORM系列-索引
- [linux内核编程入门].1.1.构建自己的内核模块
- 自己用C++写的图像处理软件试用版下载
- 构建自己的Debian
- 构建高性能ASP.NET站点之一 剖析页面的处理过程(前端)
- 异常处理----------让缺陷掌握在自己手中
- 性能比较: 事务处理控件(构建分布式应用程序)
- 为猪脸识别而进行自己数据集的构建、训练
- 处理事件的方式:两种类的覆盖处理(自己管理,覆盖专用事件函数;自己统一管理,覆盖QWidget::Event通用函数),一种对象的处理(父控件统一管理,即安装过滤器),两种全局处理(QCoreApplication安装过滤器;覆盖notify方法)
- D3D游戏编程系列(五):自己动手编写第一人称射击游戏之室外场景的构建
- D3D游戏编程系列(七):自己动手编写rpg游戏之第三人称视角的构建
- 基于功能更丰富的基础类构建您自己的 ASP.NET 页面
- 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?
- Spark 实战, 第 2 部分:使用 Kafka 和 Spark Streaming 构建实时数据处理系统
- Matconvnet 构建自己的网络
- 自己项目中的一些算法处理