柚子快报邀请码778899分享:驱动开发 nt驱动 取消列成

http://yzkb.51969.com/

1 windows驱动详解内容

在CancelIo的内部会枚举所有没有被完成的IRP,然后依次调用IoCancelIrp。另外,如果应用程序没有调用CancelIo函数,应用程序在关闭设备时同样会自动调用CancelIo。下面的代码演示了如何编写取消例程。

BOOLEAN IoCancelIrp(

_In_  PIRP Irp

);

The IoCancelIrp routine sets the cancel bit in a given IRP and calls the cancel routine for the IRP if there is one

列子:

VOID

#002 CancelReadIRP(

#003 IN PDEVICE_OBJECT DeviceObject,

#004 IN PIRP Irp

#005 )

#006 {

#007 KdPrint(("Enter CancelReadIRP\n"));

#008 PDEVICE_EXTENSION pDevExt =(PDEVICE_EXTENSION)

#009 DeviceObject->DeviceExtension;

#010 //设置完成状态为STATUS_CANCELLED

#011 Irp->IoStatus.Status =STATUS_CANCELLED;

#012 //设置IRP操作字节数

#013 Irp->IoStatus.Information =0;

#014 //结束IRP请求

#015 IoCompleteRequest( Irp, IO_NO_INCREMENT );

#016 //释放Cancel自旋锁

#017 IoReleaseCancelSpinLock(Irp->CancelIrql);

#018 KdPrint(("Leave CancelReadIRP\n"));

#019 }

#020

#pragma LOCKEDCODE

VOID

HelloDDKStartIO(

IN PDEVICE_OBJECT DeviceObject,

IN PIRP Irp

)

{

KIRQL oldirql;

DbgPrint("************entry HelloDDKStartIO Irp : %x\n", Irp);

//获取cancel自旋锁

IoAcquireCancelSpinLock(&oldirql);

if (Irp!=DeviceObject->CurrentIrp||Irp->Cancel)

{

//如果当前有正在处理的IRP,则简单的入队列,并直接返回

//入队列的工作由系统完成,在StartIO中不用负责

IoReleaseCancelSpinLock(oldirql);

DbgPrint("************Leave HelloDDKStartIO Irp : %x %x %d \n", Irp,DeviceObject->CurrentIrp,Irp->Cancel);

return;

}else

{

//由于正在处理该IRP,所以不允许调用取消例程

//因此将此IRP的取消例程设置为NULL

IoSetCancelRoutine(Irp,NULL);

IoReleaseCancelSpinLock(oldirql);

}

KEVENT event;

KeInitializeEvent(&event,NotificationEvent,FALSE);

//等3秒

LARGE_INTEGER timeout;

timeout.QuadPart = -3*1000*1000*10;

//定义一个3秒的延时,主要是为了模拟该IRP操作需要大概3秒左右时间

KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,&timeout);

Irp->IoStatus.Status = STATUS_SUCCESS;

Irp->IoStatus.Information = 0; // no bytes xfered

IoCompleteRequest(Irp,IO_NO_INCREMENT);

DbgPrint("IoCompleteRequest HelloDDKStartIO Irp : %p\n", Irp);

//在队列中读取一个IRP,并进行StartIo

IoStartNextPacket(DeviceObject,TRUE);

DbgPrint("************Leave HelloDDKStartIO Irp : %p\n", Irp);

}

#021 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,

#022 IN PIRP pIrp)

#023 {

#024 KdPrint(("Enter HelloDDKRead\n"));

#025 //获得的设备扩展

#026 PDEVICE_EXTENSION pDevExt =(PDEVICE_EXTENSION)

#027 pDevObj->DeviceExtension;

#028 //设置完成例程

#029 IoSetCancelRoutine(pIrp,CancelReadIRP);

#030 //将IRP设置为挂起

#031 //挂起IRP

#032 IoMarkIrpPending(pIrp);

#033 KdPrint(("Leave HelloDDKRead\n"));

   //返回pending状态

#035 return STATUS_PENDING;

#036 }

应用程序

#include

#include

#include

typedef struct

{

HANDLE Device;

char index;

}pare;

UINT WINAPI Thread(LPVOID context)

{

pare *pareex=(pare *)context;

printf("Enter Thread %d\n",pareex->index);

//等待5秒

OVERLAPPED overlap={0};

overlap.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

UCHAR buffer[10];

ULONG ulRead;

BOOL bRead = ReadFile(pareex->Device,buffer,10,&ulRead,&overlap);

//可以试验取消例程

CancelIo(pareex->Device);

WaitForSingleObject(overlap.hEvent,INFINITE);

printf("leave Thread %d\n",pareex->index);

return 0;

}

int main()

{

int i=0;

pare pareex[10]={0};

HANDLE hDevice =

CreateFile("\\\\.\\HelloDDK",

GENERIC_READ | GENERIC_WRITE,

FILE_SHARE_READ,

NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED

NULL );

if (hDevice == INVALID_HANDLE_VALUE)

{

printf("Open Device failed!");

return 1;

}

HANDLE hThread[10];

for(i=0;i<10;i++)

{

pareex[i].index=i;

pareex[i].Device=hDevice;

hThread[i] = (HANDLE) _beginthreadex (NULL,0,Thread,&pareex[i],0,NULL);

}

// hThread[1] = (HANDLE) _beginthreadex (NULL,0,Thread,&hDevice,0,NULL);

//主线程等待两个子线程结束

WaitForMultipleObjects(i,hThread,TRUE,INFINITE);

//创建IRP_MJ_CLEANUP IRP

CloseHandle(hDevice);

printf("process end %d\n",i);

return 0;

}

打印信息

Enter DriverUnload Enter DriverEntry Leave DriverEntry Enter HelloDDKDispatchRoutin  IRP_MJ_CREATE Leave HelloDDKDispatchRoutin Enter HelloDDKRead  8ad0cf68 ************entry HelloDDKStartIO Irp : 8ad0cf68 Enter HelloDDKRead  8b030f68 Leave HelloDDKRead  8b030f68  ************entry OnCancelIRP Irp : 8b030f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8b030f68 ************leave OnCancelIRP Irp : 8b030f68 Enter HelloDDKRead  8b23af68 Leave HelloDDKRead  8b23af68  ************entry OnCancelIRP Irp : 8b23af68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8b23af68 ************leave OnCancelIRP Irp : 8b23af68 Enter HelloDDKRead  8b130f68 Leave HelloDDKRead  8b130f68  ************entry OnCancelIRP Irp : 8b130f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8b130f68 ************leave OnCancelIRP Irp : 8b130f68 Enter HelloDDKRead  8a672f68 Leave HelloDDKRead  8a672f68  ************entry OnCancelIRP Irp : 8a672f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8a672f68 ************leave OnCancelIRP Irp : 8a672f68 Enter HelloDDKRead  8aed4f68 Leave HelloDDKRead  8aed4f68  ************entry OnCancelIRP Irp : 8aed4f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8aed4f68 ************leave OnCancelIRP Irp : 8aed4f68 Enter HelloDDKRead  8ad66f68 Leave HelloDDKRead  8ad66f68  ************entry OnCancelIRP Irp : 8ad66f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8ad66f68 ************leave OnCancelIRP Irp : 8ad66f68 Enter HelloDDKRead  8af6ef68 Leave HelloDDKRead  8af6ef68  ************entry OnCancelIRP Irp : 8af6ef68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8af6ef68 ************leave OnCancelIRP Irp : 8af6ef68 Enter HelloDDKRead  8adf6f68 Leave HelloDDKRead  8adf6f68  ************entry OnCancelIRP Irp : 8adf6f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8adf6f68 ************leave OnCancelIRP Irp : 8adf6f68 Enter HelloDDKRead  8adc6f68 Leave HelloDDKRead  8adc6f68  ************entry OnCancelIRP Irp : 8adc6f68 ************ KeRemoveEntryDeviceQueue  OnCancelIRP: 8adc6f68 ************leave OnCancelIRP Irp : 8adc6f68 IoCompleteRequest HelloDDKStartIO Irp : 8AD0CF68 ************Leave HelloDDKStartIO Irp : 8AD0CF68 Leave HelloDDKRead  8ad0cf68  Enter HelloDDKDispatchRoutin  IRP_MJ_CLEANUP Leave HelloDDKDispatchRoutin Enter HelloDDKDispatchRoutin  IRP_MJ_CLOSE Leave HelloDDKDispatchRoutin  

应用打印

C:\Documents and Settings\ygc>D:\chapte Enter Thread 0 Enter Thread 1 Enter Thread 2 Enter Thread 3 Enter Thread 4 Enter Thread 5 Enter Thread 6 Enter Thread 7 Enter Thread 8 Enter Thread 9 leave Thread 4 leave Thread 1 leave Thread 6 leave Thread 8 leave Thread 3 leave Thread 7 leave Thread 2 leave Thread 9 leave Thread 5 leave Thread 0 process end  10

C:\Documents and Settings\ygc>

IoCancelIrp在内部会首先获得该自旋锁,IoCancelIrp会调用取消回调例程,因此,释放该自旋锁的任务就留给了取消回调例程。获得取消自旋的函数是IoAcquire CancelSpinLock函数,而释放取消自旋锁的函数是IoReleaseCancelSpinLock函数  

在设置取消例程中要注意同步问题是,当退出取消例程时,一定要释放cancel自旋锁,否则会导致系统崩溃。另外,cancel自旋锁是全局自旋锁,所有驱动程序都会使用这个自旋锁,因此,占用自旋锁时间不宜过长

2 在IoStartPacket内设置取消函数

取消函数调用流程图

下面的代码KeLowerIrql,为什么会调

IoReleaseCancelSpinLock(pIrp->CancelIrql);//因为释放,导致取消列成函数不运行在DISPATCH_LEVEL,调用IoStartPacket函数后必须要降低运行IRQ(pass_level)

A driver must hold the system cancel spin lock when calling this routine if the driver uses the I/O manager-supplied device queue in the device object. The driver runs at IRQL = DISPATCH_LEVEL after calling IoAcquireCancelSpinLock until it releases the cancel spin lock with IoReleaseCancelSpinLock.

  KeLowerIrql(oldirql);//其实他要降低IRQL的级别是因为在IoStartPacket函数中如果设备是空闲的话呢会将IRQL的级别调节成DISPATCH_LEVEL

OID OnCancelIRP(PDEVICE_OBJECT DeviceObject, PIRP pIrp){

2

3 UINT32* InputBuffer = (UINT32*)pIrp->AssociatedIrp.SystemBuffer;

4 DbgPrint("Enter OnCancelIRP:%d!\n", *InputBuffer);

5 if (pIrp == DeviceObject->CurrentIrp){

6 //此IRP不在队列中,正在由StartIO处理

7 DbgPrint("Current IRP OnCancelIRP!\n");

8 KIRQL oldirql = pIrp->CancelIrql;//保存原先的IRQL

9 IoReleaseCancelSpinLock(pIrp->CancelIrql);

10 IoStartNextPacket(DeviceObject, TRUE);

11 KeLowerIrql(oldirql);//其实他要降低IRQL的级别是因为在IoStartPacket函数中如果设备是空闲的话呢会将IRQL的级别调节成DISPATCH_LEVEL

12 }

13 else{//当irp != fdo->CurrentIrp的时候,这个很好理解,

14 //就是需求取消的irp还没有被执行,那么也就是说还在队列里面,直接把这个irp从队列里面删除就可以了。

15 DbgPrint("Uncurrent IRP OnCancelIRP!\n");

16 KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,

17 &pIrp->Tail.Overlay.DeviceQueueEntry);

18 IoReleaseCancelSpinLock(pIrp->CancelIrql);

19 }

20 pIrp->IoStatus.Status = STATUS_CANCELLED;

21 pIrp->IoStatus.Information = 0;

22 IoCompleteRequest(pIrp, IO_NO_INCREMENT);

23 DbgPrint("Leave OnCancelIRP:%d!\n", *InputBuffer);

24 }

首先因为I/O管理器在调用取消例程前会先调用IoAcquireCancelSpinLock来获取自旋锁,那么在取消例程里面切记一定要调用IoReleaseCancelSpinLock来释放自旋锁。

取消例程里面有个判断语句if (Irp == DeviceObject->CurrentIrp),

1. 当irp != fdo->CurrentIrp的时候,这个很好理解,就是需求取消的irp还没有被执行,那么也就是说还在队列里面,直接把这个irp从队列里面删除就可以了。

2. 当irp == fdo->CurrentIrp的时候,这是个有趣的时间点,这个时间点处于fdo->CurrentIrp=Irp(IoStartPacket或者IoStartNextPacket)和IoAcquireCancelSpinLock(StartIo例程)之间。因为需要取消的irp已经不在队列中了,那么就无需操作队列。在取消例程里面只要调用IoStartNextPacket就可以了(也就是跳过了当前irp的处理)https://blog.csdn.net/zj510/article/details/8287712

链接位置:《Windows驱动开发技术详解》之StartIO例程 - _No.47 - 博客园 (cnblogs.com)

柚子快报邀请码778899分享:驱动开发 nt驱动 取消列成

http://yzkb.51969.com/

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: