21
1月
2016

4、Windows的设备驱动框架 -IRP

IRP数据结构中的许多字段和成分需要结合代码和具体实例来说明,这里只能先说个大概。

其实数据结构IRP只是"I/O请求包"IRP的头部,在IRP数据结构的后面还有一个IO_STACK_LOCATION数据结构的数组,数组的大小则取决于IRP数据结构中的StackCount,其数值来自堆叠中顶层设备对象的StackSize字段。这样,就在IRP中为目标设备对象堆叠中的每一层即每个模块都准备好了一个IO_STACK_LOCATION数据结构。而CurrentLocation,则是用于该数组的下标,说明目前是在堆叠中的哪一层,因而正在使用哪一个IO_STACK_LOCATION数据结构。这就好像是一叠"任务单",其中有的(至少一个)是已经填写好了的,其余的可以由下层模块根据具体情况填写。而下层模块的操作结果,则也是通过相应的"任务单"返回到上层。特别地,结构成分IoStatus是个IO_STATUS_BLOCK即"I/O状态块",用来返回包括状态码在内的有关信息。

在某些特殊的情况下,中间层模块也可以选择不下传来自上层的IRP,而另行准备一个IRP下传,此时来自上层的IRP为"主IRP(Master IRP)",由中间层模块生成的IRP为"相关IRP(Associated IRP)",其指针MasterIrp用来指向主IRP。

既然针对堆叠中的每个模块都有其自己的IO_STACK_LOCATION数据结构,可想而知IRP数据结构中的信息是全局性的、为堆叠中所有模块所共享的。所以,Apc显然是为整个异步I/O操作的返回而设的,CompletionKey则用来区分是从哪一次异步I/O操作返回,UserEvent当然是指向用于同步的EVENT对象。Type的值应该是IO_TYPE_IRP,而Size表示包括IO_STACK_LOCATION结构数组在内的整个IRP的大小。

再看IO_STACK_LOCATION数据结构:

 

1.typedef struct _IO_STACK_LOCATION { 
2. UCHAR MajorFunction; //主功能码 
3.UCHAR MinorFunction; //次功能码 
4.UCHAR Flags; 
5. UCHAR Control; 
6. // The following user parameters are based on the service that is being 
7. // invoked. Drivers and file systems can determine which set to use based 
8. // on the above major and minor function codes. 
9. union { 
10. // System service parameters for: NtCreateFile 
11. struct { 
12. PIO_SECURITY_CONTEXT SecurityContext; 
13. ULONG Options; 
14. USHORT POINTER_ALIGNMENT FileAttributes; 
15. USHORT ShareAccess; 
16. ULONG POINTER_ALIGNMENT EaLength; 
17. } Create; 
18. ...... 
19. struct { 
20. ULONG Length; 
21. ULONG POINTER_ALIGNMENT Key; 
22. LARGE_INTEGER ByteOffset; 
23. } Write; 
24. ...... 
25. } Parameters; 
26. // Save a pointer to this device driver's device object for this request 
27. // so it can be passed to the completion routine if needed. 
28. PDEVICE_OBJECT DeviceObject; 
29. // The following location contains a pointer to the file object for this 
30. PFILE_OBJECT FileObject; 
31. // The following routine is invoked depending on the flags in the above flags field. 
32. PIO_COMPLETION_ROUTINE CompletionRoutine; 
33. // The following is used to store the address of the context parameter 
34. // that should be passed to the CompletionRoutine. 
35. PVOID Context; 
36.} IO_STACK_LOCATION; 

字段MajorFunction和MinorFunction是操作码。在同一次宏观的设备操作中,堆叠中不同层次的模块所要执行的微观的操作可能是不同的,所以具体的操作码在IO_STACK_ LOCATION结构中,而不是在IRP结构中。操作码分成主、次两部分,其中主操作码MajorFunction的值,例如IRP_MJ_CREATE、IRP_MJ_CREATE_NAMED_PIPE、IRP_MJ_ READ、IRP_MJ_SHUTDOWN、IRP_MJ_POWER等,是由系统定义的,每个驱动模块都要为这些操作提供函数指针。而次操作码MinorFunction则由各驱动模块自行定义。

字段MajorFunction和MinorFunction是操作码。在同一次宏观的设备操作中,堆叠中不同层次的模块所要执行的微观的操作可能是不同的,所以具体的操作码在IO_STACK_ LOCATION结构中,而不是在IRP结构中。操作码分成主、次两部分,其中主操作码MajorFunction的值,例如IRP_MJ_CREATE、IRP_MJ_CREATE_NAMED_PIPE、IRP_MJ_ READ、IRP_MJ_SHUTDOWN、IRP_MJ_POWER等,是由系统定义的,每个驱动模块都要为这些操作提供函数指针。而次操作码MinorFunction则由各驱动模块自行定义。

前面讲过,Windows的设备驱动是面向异步操作的。这是什么意思呢?在Windows的设备驱动框架中,对于每一层设备驱动的调用都可以是异步的,当前线程在每一层上都可以把IRP暂时扣留而先返回上一层,以后再由DPC函数或内核线程下传IRP或完成IRP所要求的操作。而上一层设备驱动,则可以选择再向上返回或等待下层操作的完成,如果上一层要求同步操作就通过KeWaitForSingleObject()在一个事件对象上等待。那么,如果在某一层上执行的是异步操作,并且因此已经先返回上层了,到IRP从下一层返回的时候怎么办呢?还进不进这一层呢?事实上,当操作结束时每一层都可能有些善后事宜需要处理。为此,如果需要的话,就要提供一个本层的善后函数,使得可以在IRP从下层返回时加以调用。于是,原则上每一层都需要提供这么一个函数,IO_STACK_LOCATION中的指针CompletionRoutine正是用来提供这个函数的。而另一个指针Context,则用来为此提供"上下文"数据实际上是参数。

Categories: 驱动开发

Overall Rating (0)

0 out of 5 stars

Leave your comments

Post comment as a guest

0 Character restriction
Your text should be more than 3 characters
  • No comments found