当一个应用程序调用函数去操作某个设备时,比如调用createFile,deviceIOControl,等等时,I/O管理器为此函数创建一个IRP数据结构对象和一个IRP_STACK_LOCATION数据结构对象数组。
(数组个数等于驱动程序堆栈上驱动的个数)。IRP对象中的数据成员是已经被填充好了的,其中有一个CurrentLocation是当前IRP_STACK_LOCATION堆栈单元的索引,Tail.Overlay.CurrentStackLocation
是当前IRP_STACK_LOCATION单元的指针。
一开始的时候,它当然是指向IRP_STACK_LOCATION数组的第一个元素。
这调用某个驱动程序的分发函数时,IRP作为参数传给了该分发函数,这样,该函数就可以访问IRP中的Tail.Overlay.CurrentStackLocation, 这样就可以访问IRP_STACK_LOCATION的
成员了(当然我们不需要直接这样做,而是调用IoGetCurrentStackLocation来实现)。因为OS并不会为我们初始化IRP_STACK_LOCATION数组中的每一个对象,所以初始化下一层驱动要使用的IRP_STACK_LOCATION就由它的上一层驱动来完成。要初始化它们,首先要找到它们,方法是调用IoGetNextStackLocation(它内部实现只是将CurrentStackLocation++),得到下一个IRP_STACK_LOCATION
对象指针。然后为其赋值。然后调用IOCALLDRIVER().
理解的关键点是:
一 OS为我们生成IRP,IPR_STACK_LOCATION数组,注意,是个数组。IRP中的CurrentStackLocation指向IRP_STACK_LOCATION中的某一个元素。
二 OS 并不会为我们填充好IRP_STACK_LOCATION数组,每一个元素是由上一层驱动负责填充的。
三 IRP_STACK_LOCATION数组,它们的元素之间不需要指针联系。
四 OS只负责把IRP包给最上层的驱动程序,至于如何向下层,就是驱动程序自己的事情了。所以下层对就的那些IRP_STACK_LOCATION,完全由上层驱动函数负责填充,OS不管。
驱动程序如何填充一层驱动需要用的IRP_STACK_LOCATION呢?
可以通过调用IoGetNextIrpStackLocation调用得到。其实该函数内部就是返回CurrentStackLocation加1而已。对数组值加1,当然就是得到数组的下一个值了。
这样就可以对它进行赋值了。然后调用IoCallDriver(),IoCallDriver()会将irp包中的CurrentStackLocation值加1,然后调用那个DRIVER.
如果下一层的驱动需要的IRP_STACK_LOCATION和本层驱动的一样,则可以直接调用IoSkipCurrentIrpStackLocation或IoCopyCurrentIrpStackLocationToNext.
IoSkipCurrentIrpStackLocation将CurrentStackLocation减1. 正好与IoCallDriver的加1抵消。所以相当于下层驱动和本层驱动用的是同一个IRP_STACK_LOCATIN元素。