柚子快報(bào)邀請碼778899分享:驅(qū)動(dòng)開發(fā) nt驅(qū)動(dòng) 取消列成
柚子快報(bào)邀請碼778899分享:驅(qū)動(dòng)開發(fā) nt驅(qū)動(dòng) 取消列成
1?。鳎椋睿洌铮鳎篁?qū)動(dòng)詳解內(nèi)容
在CancelIo的內(nèi)部會(huì)枚舉所有沒有被完成的IRP,然后依次調(diào)用IoCancelIrp。另外,如果應(yīng)用程序沒有調(diào)用CancelIo函數(shù),應(yīng)用程序在關(guān)閉設(shè)備時(shí)同樣會(huì)自動(dòng)調(diào)用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 //設(shè)置完成狀態(tài)為STATUS_CANCELLED
#011 Irp->IoStatus.Status =STATUS_CANCELLED;
#012 //設(shè)置IRP操作字節(jié)數(shù)
#013 Irp->IoStatus.Information =0;
#014 //結(jié)束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)
{
//如果當(dāng)前有正在處理的IRP,則簡單的入隊(duì)列,并直接返回
//入隊(duì)列的工作由系統(tǒng)完成,在StartIO中不用負(fù)責(zé)
IoReleaseCancelSpinLock(oldirql);
DbgPrint("************Leave HelloDDKStartIO Irp : %x %x %d \n", Irp,DeviceObject->CurrentIrp,Irp->Cancel);
return;
}else
{
//由于正在處理該IRP,所以不允許調(diào)用取消例程
//因此將此IRP的取消例程設(shè)置為NULL
IoSetCancelRoutine(Irp,NULL);
IoReleaseCancelSpinLock(oldirql);
}
KEVENT event;
KeInitializeEvent(&event,NotificationEvent,FALSE);
//等3秒
LARGE_INTEGER timeout;
timeout.QuadPart = -3*1000*1000*10;
//定義一個(gè)3秒的延時(shí),主要是為了模擬該IRP操作需要大概3秒左右時(shí)間
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);
//在隊(duì)列中讀取一個(gè)IRP,并進(jìn)行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 //獲得的設(shè)備擴(kuò)展
#026 PDEVICE_EXTENSION pDevExt =(PDEVICE_EXTENSION)
#027 pDevObj->DeviceExtension;
#028 //設(shè)置完成例程
#029 IoSetCancelRoutine(pIrp,CancelReadIRP);
#030 //將IRP設(shè)置為掛起
#031 //掛起IRP
#032 IoMarkIrpPending(pIrp);
#033 KdPrint(("Leave HelloDDKRead\n"));
//返回pending狀態(tài)
#035 return STATUS_PENDING;
#036 }
應(yīng)用程序
#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);
//可以試驗(yàn)取消例程
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,//此處設(shè)置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);
//主線程等待兩個(gè)子線程結(jié)束
WaitForMultipleObjects(i,hThread,TRUE,INFINITE);
//創(chuàng)建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 ?
應(yīng)用打印
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在內(nèi)部會(huì)首先獲得該自旋鎖,IoCancelIrp會(huì)調(diào)用取消回調(diào)例程,因此,釋放該自旋鎖的任務(wù)就留給了取消回調(diào)例程。獲得取消自旋的函數(shù)是IoAcquire CancelSpinLock函數(shù),而釋放取消自旋鎖的函數(shù)是IoReleaseCancelSpinLock函數(shù) ?
在設(shè)置取消例程中要注意同步問題是,當(dāng)退出取消例程時(shí),一定要釋放cancel自旋鎖,否則會(huì)導(dǎo)致系統(tǒng)崩潰。另外,cancel自旋鎖是全局自旋鎖,所有驅(qū)動(dòng)程序都會(huì)使用這個(gè)自旋鎖,因此,占用自旋鎖時(shí)間不宜過長
2 在IoStartPacket內(nèi)設(shè)置取消函數(shù)
取消函數(shù)調(diào)用流程圖
下面的代碼KeLowerIrql,為什么會(huì)調(diào)
IoReleaseCancelSpinLock(pIrp->CancelIrql);//因?yàn)獒尫牛瑢?dǎo)致取消列成函數(shù)不運(yùn)行在DISPATCH_LEVEL,調(diào)用IoStartPacket函數(shù)后必須要降低運(yùn)行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);//其實(shí)他要降低IRQL的級別是因?yàn)樵贗oStartPacket函數(shù)中如果設(shè)備是空閑的話呢會(huì)將IRQL的級別調(diào)節(jié)成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不在隊(duì)列中,正在由StartIO處理
7 DbgPrint("Current IRP OnCancelIRP!\n");
8 KIRQL oldirql = pIrp->CancelIrql;//保存原先的IRQL
9 IoReleaseCancelSpinLock(pIrp->CancelIrql);
10 IoStartNextPacket(DeviceObject, TRUE);
11 KeLowerIrql(oldirql);//其實(shí)他要降低IRQL的級別是因?yàn)樵贗oStartPacket函數(shù)中如果設(shè)備是空閑的話呢會(huì)將IRQL的級別調(diào)節(jié)成DISPATCH_LEVEL
12 }
13 else{//當(dāng)irp != fdo->CurrentIrp的時(shí)候,這個(gè)很好理解,
14 //就是需求取消的irp還沒有被執(zhí)行,那么也就是說還在隊(duì)列里面,直接把這個(gè)irp從隊(duì)列里面刪除就可以了。
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 }
首先因?yàn)镮/O管理器在調(diào)用取消例程前會(huì)先調(diào)用IoAcquireCancelSpinLock來獲取自旋鎖,那么在取消例程里面切記一定要調(diào)用IoReleaseCancelSpinLock來釋放自旋鎖。
取消例程里面有個(gè)判斷語句if (Irp == DeviceObject->CurrentIrp),
1. 當(dāng)irp != fdo->CurrentIrp的時(shí)候,這個(gè)很好理解,就是需求取消的irp還沒有被執(zhí)行,那么也就是說還在隊(duì)列里面,直接把這個(gè)irp從隊(duì)列里面刪除就可以了。
2. 當(dāng)irp == fdo->CurrentIrp的時(shí)候,這是個(gè)有趣的時(shí)間點(diǎn),這個(gè)時(shí)間點(diǎn)處于fdo->CurrentIrp=Irp(IoStartPacket或者IoStartNextPacket)和IoAcquireCancelSpinLock(StartIo例程)之間。因?yàn)樾枰∠膇rp已經(jīng)不在隊(duì)列中了,那么就無需操作隊(duì)列。在取消例程里面只要調(diào)用IoStartNextPacket就可以了(也就是跳過了當(dāng)前irp的處理)https://blog.csdn.net/zj510/article/details/8287712
鏈接位置:《Windows驅(qū)動(dòng)開發(fā)技術(shù)詳解》之StartIO例程 - _No.47 - 博客園 (cnblogs.com)
柚子快報(bào)邀請碼778899分享:驅(qū)動(dòng)開發(fā) nt驅(qū)動(dòng) 取消列成
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。