在做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。
构建自己的IRP来处理I/O
最新推荐文章于 2022-04-26 11:24:18 发布