Windows驱动开发WDM (14)- 分层驱动(设备栈,I/O栈)

前面的文章介绍了一个简单的功能驱动和过滤驱动。它们都是用WDM模型写的驱动,在写WDM驱动的时候,我们经常会碰到PDO和FDO。PDO就是物理设备对象,FDO是功能设备对象。FDO是附在PDO上面的,当然FDO上面还可以有其他的FDO。这样就构成了一个栈,就是设备栈。所以说WDM驱动天生就是分层驱动程序,最起码它有PDO和FDO。其实WDM驱动就是从分层的NT驱动发展过来的。

 

设备栈(Device Stack)

当我们在WDM驱动的AddDevice里面创建一个设备对象时,一定会调用这个函数:IoAttachDeviceToDeviceStack。

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
  _In_  PDEVICE_OBJECT SourceDevice,
  _In_  PDEVICE_OBJECT TargetDevice
);

这个参数的作用就是将SourceDevice附在TargetDevice上面,通常TargetDevice是AddDevice传进来的PDO。如果PDO上面还有其他FDO,比如过滤驱动啥的,那么SourceDevice将会附在这些过滤驱动设备对象上面。SourceDevice就是新创建的FDO。这个参数的返回值是SourceDevice的下层设备对象,也就是说返回的设备对象的上面就是新创建的FDO(SourceDevice)。
通过IoAttachDeviceToDeviceStack函数,可以构成一个设备栈。比如前面讲过滤驱动的时候(http://blog.csdn.net/zj510/article/details/8332658),我们就将过滤驱动的设备对象通过IoAttachDeviceToDeviceStack附在了功能驱动设备对象(http://blog.csdn.net/zj510/article/details/8295453)上面。这个功能驱动创建了3个设备对象,那么过滤驱动就在最上层的功能设备对象的上面。画图加强记忆:

Windows驱动开发WDM (14)- 分层驱动(设备栈,I/O栈)_第1张图片

 

这个图里面画的是不同的驱动创建的不同设备对象构成的设备栈。其实同一个驱动也可以创建多个设备构成设备栈(需不需要这么做是另外一回事情)。

那么设备栈会起什么作用呢?比如用户模式的caller打开了DeviceA,并且向这个设备发生一个IRP,那么这个IRP会先经过DeviceC,然后到达DeviceA。IRP的过程是:

DeviceC -> DeviceB -> DeviceA -> PDO

 

I/O栈 (I/O Stack)

除了设备栈以外,还有另外一个概念:I/O栈。I/O栈用IO_STACK_LOCATION数据结构来表示。I/O栈和设备栈紧密联系。当用户模式的应用程序发起IRP请求的时候,IRP会先到达设备栈的顶层,然后层层往下传递。传递的数据结构就是IRP。IRP的数据结构里面存储着IO_STACK_LOCATION数组的一个指针。IRP每穿过一次设备堆栈,就会用IO_STACK_LOCATION记录本次操作的某些属性。IO栈是和设备栈一一对应的,看图:

Windows驱动开发WDM (14)- 分层驱动(设备栈,I/O栈)_第2张图片

 

 

IRP处理

当顶层的设备对象收到IRP请求时,有多种方式来处理IRP:

1. 直接处理该irp,调用IoCompleteRequest;

2. 调用StartIo,让系统将IRP串行化处理;

3. 将irp传给下层驱动,让下层驱动来处理该irp。

每个设备对象都对应着一个IO_STACK_LOCATION。IRP内部有个指针指向正在使用的IO_STACK_LOCATION, 可以使用IoGetCurrentIrpStackLocation来获得当前I/O堆栈信息。

有两种方式来调用下层驱动:

1. 调用IoSkipCurrentIrpStackLocation和IoCallDriver。通常如果当前设备对象不需要处理irp,我们可以这么调用。

2. 调用IoCopyCurrentIrpStackLocationToNext和IoCallDriver。通常如果当前设备也需要处理irp,那么我们可以等处理完后,将irp拷贝到下层驱动,然后再调用IoCallDriver。

 

遍历设备栈

怎么来遍历整个设备栈呢?分两种情况,从下往上和从上往下:

1. 从下往上,easy,DeviceObject::AttachedDevice就保存了当前对象上面的对象。

2. 从上往下,需要借助设备扩展,当我们调用IoAttachDeviceToDeviceStack的时候会返回下层设备对象,我们可以把这个对象保存到设备扩展里面,这样遍历的时候就可以从设备扩展里面得到下层设备对象。

 

总结

现在我们大多数情况下都是采用分层驱动来设计我们的系统。这样比较灵活,同时也可以将不同的功能分散到不同的驱动中。然后可以在设备栈里面层层传递irp,选择某个设备处理irp。

你可能感兴趣的:(Windows驱动开发WDM (14)- 分层驱动(设备栈,I/O栈))