处理取消IRP

IRP通过KDevice的分发例程进入驱动程序。驱动程序可以立即处理它并返回状态信息,或者是将它排队并返回STATUS_PENDING

当一个IRP被排队,驱动程序应该使这个IRP可以取消,因为如果这个IRP的发起者提出一个取消请求,I/O管理器应该可以通知驱动程序响应这个请求。如果一个IRP拥有一个取消例程,则它是可以取消的。取消例程可以通过KDevice::QueueIrp, KIrp::TestAndSetCancelRoutine, KIrp::SetCancelRoutine进行设置。当驱动程序用KDevice::QueueIrp来排队IRP时,必须决定这个IRP是否可以取消。而用KDriverManagedQueue::QueueIrp排队的IRP总是可以取消的。

如果当一个请求已经被处理到驱动程序不能再取消它时,必须将这个请求的取消例程设置为NULL(用KIrp::TestAndSetCancelRoutineKIrp::SetCancelRoutine)。当一个IRP可以取消时驱动程序不能完成它。如果一个IRP的取消标志被设置时就没有必要清除取消例程。

 

下面是完成取消例程的步骤:

       KDevice子类的声明中,使用DEVMEMBER_CANCELIRP宏把取消例程声明为设备类(即KDevice的子类,译注)的成员函数。一些驱动开发者可能会发现用不同的取消成员函数处理不同种类的IRP或过程的不同阶段是很有用的。

 

class MyDevice : public KDevice
{
   public:
     MyDevice(void);
     . . .
     DEVMEMBER_CANCELIRP(MyDevice, Cancel)
 };

 

当你用KDevice::QueueIrp排队一个IRP时,将第二个参数设置为LinkTo(Cancel),这样可以连接IRP和设备类的静态函数(即取消例程,译注),这个函数是设备类的声明里用DEVMEMBER_CANCELIRP宏声明的非静态成员。

 

QueueIrp( I, LinkTo(Cancel) );

 

书写取消成员函数。当系统调用它时,它保存系统范围内的取消自旋锁,必须在完成这个请求之前释放。取消自旋锁在取消例程返回后不能继续保存。

 

VOID MyDevice::Cancel(KIrp I)
{
   if ( PIRP(I) == CurrentIrp() )
   {
     CancelSpinLock::Release(I->CancelIrql);
     I.Information() = 0;
     I.Status() = STATUS_CANCELLED;
     NextIrp(I);
   }
   else
   {
     KDeviceQueue dq(DeviceQueue());
     BOOLEAN bRemoved = dq.RemoveSpecificEntry(I);
     CancelSpinLock::Release(I->CancelIrql);
     if ( bRemoved )
     {
       I.Information() = 0;
       I.Complete(STATUS_CANCELLED);
     }
   }
}

在你的StarIo函数内,你必须检查取消标志并且清除取消例程。使用KIrp::TestAndSetCancelRoutine函数来完成:

 

VOID MyDevice::StartIo(KIrp I)
{
   if ( ! TestAndSetCancelRoutine(LinkTo(Cancel), NULL, CurrentIrp() )
     return;
   . . .
   // normal IRP processing
   . . .
}

 注意,强烈建议你给QueueIrp传递非空取消例程,但这不是必须的。如果你排队一个空取消历程的IRP,你必须始终在保存全局取消自旋锁时,在StartIo函数的开始部分测试取消标志。你可以调用TestAndSetCancelRoutine函数并把前两个参数设置为NULL完成这个功能。如果函数返回FALSE,你必须用STATUS_CANCELLED状态结束IRP,因为排队时没有有效的取消例程。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页