在Windows分层驱动模型中,设备栈中的设备一般都是通过对上层传来的IRP做相应的处理来实现驱动的功能。这里对常用的几种IRP传递及完成的方式进行归纳和总结:
1. 在本层驱动中完成
1.1 在本层驱动中以同步方式完成
在本层同步完成一般做完相应处理后,设置Irp->IoStatus.Status和Irp->IoStatus.Information,调用IoCompleteRequest完成该IRP,return IRP的完成状态即可。
1.2 在本层驱动中以异步方式完成
在本层异步完成一般是得到IRP后将其入队/起线程另行处理,同时调用IoMarkIrpPending将该IRP标记为Pending,之后即可return STATUS_PENDING。此时该IRP并未真正完成,需待未决的操作在他处完成后调用IoCompleteRequest才真正完成。
2. 转发至下层驱动
2.1 本层不作处理
有时对于某些IRP,本层驱动不需要做任何处理。此时可调用IoSkipCurrentIrpStackLocation跳过当前设备栈,然后调用IoCallDriver将IRP转发至下层驱动,并将转发的结果直接返回。此种处理方式并不需要关心下层驱动处理IRP的方式究竟是同步还是异步。IRP转发至下层后就与己无关了。
2.2 同步转发方式
有时IRP在本层无法直接处理,需要将其转发至下层,待下层处理完后在其结果上进行修改再将其返回。这时可以采用同步转发方式进行处理。首先在相应dispatch routine中初始化一个未激发的event,调用IoCopyCurrentIrpStackLocationToNext将本层设备栈参数复制到下层。然后设置一个CompletionRoutine,将刚才初始化过的event作为context传给它。之后调用IoCallDriver转发IRP至下层,并判断返回值是否为STATUS_PENDING。是则wait之前的event。在CompletionRoutine中判断该IRP是否PendingReturned,是则说明之前IoCallDriver返回了STATUS_PENDING,于是激发event。CompletionRoutine返回STATUS_MORE_PROCESSING_REQUIRED使我们的dispatch routine重新取得对该IRP的控制权。本层dispatch等待结束再次获得控制权后,进行相应处理,之后需再次调用IoCompleteRequest完成该IRP。
同步转发是驱动中常用的一种IRP处理方式。一般会将本层dispatch转发IRP至下层并等待CompletionRoutine激发event的行为独立成一个ForwardIrpSynchronous的函数。几个dispatch只需一个ForwardIrpSynchronous,代码相对简单。
注意不要在本层dispatch中调用IoMarkIrpPending,因为上层的请求在本层被同步处理了。在同步转发中,如果下层驱动也采用同步方式处理,则本层dispatch不会(也不需要)wait,IoCallDriver返回时CompletionRoutine已经被调用,性能上也没有什么损失。
2.3 异步转发方式
异步转发也能在下层驱动完成IRP时获得处理的机会,其主要是采用了异步处理机制。首先本层dispatch调用IoCopyCurrentIrpStackLocationToNext将本层设备栈参数复制到下层,设置相应的CompletionRoutine,然后调用IoCallDriver将IRP转发至下层驱动,并将转发的结果直接return给上层调用者。在CompletionRoutine中再判断该IRP是否PendingReturned,是则需要调用IoMarkIrpPending。之后可对下层驱动处理该IRP的结果进行相应操作。最后返回STATUS_CONTINUE_COMPLETION(同STATUS_SUCCESS)。
异步转发在异步处理时性能最佳,但处理的逻辑放在了CompletionRoutine中,因此多个dispatch需要编写多个CompletionRoutine。而同步转发往往几个dispatch只需一个ForwardIrpSynchronous即可,代码相对简单。
1. 在本层驱动中完成
1.1 在本层驱动中以同步方式完成
在本层同步完成一般做完相应处理后,设置Irp->IoStatus.Status和Irp->IoStatus.Information,调用IoCompleteRequest完成该IRP,return IRP的完成状态即可。
1.2 在本层驱动中以异步方式完成
在本层异步完成一般是得到IRP后将其入队/起线程另行处理,同时调用IoMarkIrpPending将该IRP标记为Pending,之后即可return STATUS_PENDING。此时该IRP并未真正完成,需待未决的操作在他处完成后调用IoCompleteRequest才真正完成。
2. 转发至下层驱动
2.1 本层不作处理
有时对于某些IRP,本层驱动不需要做任何处理。此时可调用IoSkipCurrentIrpStackLocation跳过当前设备栈,然后调用IoCallDriver将IRP转发至下层驱动,并将转发的结果直接返回。此种处理方式并不需要关心下层驱动处理IRP的方式究竟是同步还是异步。IRP转发至下层后就与己无关了。
2.2 同步转发方式
有时IRP在本层无法直接处理,需要将其转发至下层,待下层处理完后在其结果上进行修改再将其返回。这时可以采用同步转发方式进行处理。首先在相应dispatch routine中初始化一个未激发的event,调用IoCopyCurrentIrpStackLocationToNext将本层设备栈参数复制到下层。然后设置一个CompletionRoutine,将刚才初始化过的event作为context传给它。之后调用IoCallDriver转发IRP至下层,并判断返回值是否为STATUS_PENDING。是则wait之前的event。在CompletionRoutine中判断该IRP是否PendingReturned,是则说明之前IoCallDriver返回了STATUS_PENDING,于是激发event。CompletionRoutine返回STATUS_MORE_PROCESSING_REQUIRED使我们的dispatch routine重新取得对该IRP的控制权。本层dispatch等待结束再次获得控制权后,进行相应处理,之后需再次调用IoCompleteRequest完成该IRP。
同步转发是驱动中常用的一种IRP处理方式。一般会将本层dispatch转发IRP至下层并等待CompletionRoutine激发event的行为独立成一个ForwardIrpSynchronous的函数。几个dispatch只需一个ForwardIrpSynchronous,代码相对简单。
注意不要在本层dispatch中调用IoMarkIrpPending,因为上层的请求在本层被同步处理了。在同步转发中,如果下层驱动也采用同步方式处理,则本层dispatch不会(也不需要)wait,IoCallDriver返回时CompletionRoutine已经被调用,性能上也没有什么损失。
2.3 异步转发方式
异步转发也能在下层驱动完成IRP时获得处理的机会,其主要是采用了异步处理机制。首先本层dispatch调用IoCopyCurrentIrpStackLocationToNext将本层设备栈参数复制到下层,设置相应的CompletionRoutine,然后调用IoCallDriver将IRP转发至下层驱动,并将转发的结果直接return给上层调用者。在CompletionRoutine中再判断该IRP是否PendingReturned,是则需要调用IoMarkIrpPending。之后可对下层驱动处理该IRP的结果进行相应操作。最后返回STATUS_CONTINUE_COMPLETION(同STATUS_SUCCESS)。
异步转发在异步处理时性能最佳,但处理的逻辑放在了CompletionRoutine中,因此多个dispatch需要编写多个CompletionRoutine。而同步转发往往几个dispatch只需一个ForwardIrpSynchronous即可,代码相对简单。
值得注意的是,各个dispatch routine运行的IRQL是由调用关系决定的。如果上层调用者有运行在DISPATCH_LEVEL的可能,则本层的dispatch也需要按照运行在DISPATCH_LEVEL来设计。比如传递至本层dispatch的IRP是在上层驱动的StartIO例程中转发的,则本层处理该类IRP的代码就可能运行在DISPATCH_LEVEL。
转自:http://www.cnblogs.com/spinsoft/archive/2012/06/26/2563808.html