国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

IRP的同步

2019-11-11 01:47:50
字體:
供稿:網(wǎng)友

應(yīng)用層對設(shè)備的同步與異步操作

以WriteFile為例,一般的同步操作是調(diào)用WriteFile完成后,并不會(huì)返回,應(yīng)用程序會(huì)在此處暫停,一直等到函數(shù)將數(shù)據(jù)寫入文件中并正常返回,而異步操作則是調(diào)用WriteFile后會(huì)馬上返回,但是操作系統(tǒng)有另一線程在繼續(xù)執(zhí)行寫的操作,這段時(shí)間并不影響應(yīng)用程序的代碼往下執(zhí)行,一般異步操作都有一個(gè)事件用來通知應(yīng)用程序,異步操作的完成,以下圖分別來表示同步和異步操作: 這里寫圖片描述 這里寫圖片描述 在調(diào)用這些函數(shù)時(shí)可以看做操作系統(tǒng)提供一個(gè)專門的線程來處理,然后如果選擇同步,那么應(yīng)用層線程會(huì)等待底層的線程處理完成后接著執(zhí)行才執(zhí)行后面的操作,而異步則是應(yīng)用層線程接著執(zhí)行后面的代碼,而由操作系統(tǒng)來通知,因此一般來說異步相比較與同步少去了等待操作返回的過程,效率更高一些,但是選擇同步還是異步,應(yīng)該具體問題具體分析

同步操作設(shè)備

如果需要對設(shè)備進(jìn)行同步操作,那么在使用CreateFile時(shí)就需要以同步的方式打開,這個(gè)函數(shù)的第六個(gè)參數(shù)dwFlagsAndAttributes是同步和異步操作的關(guān)鍵,如果給定了參數(shù)FILE_FLAG_OVERLAPPED則是異步的,否則是同步的。一旦用這個(gè)函數(shù)指定了操作方式,那么以后在使用這個(gè)函數(shù)返回的句柄進(jìn)行操作時(shí)就是該中操作方式,但是這個(gè)函數(shù)本身不存在異步操作方式,一來這個(gè)函數(shù)沒有什么耗時(shí)的操作,二來,如果它不正常返回,那么針對這個(gè)設(shè)備的操作也不能進(jìn)行。 一般像WriteFile、ReadFile、DeviceIoControl函數(shù)最后一個(gè)參數(shù)lpOverlapped,是一個(gè)OVERLAPPED類型的指針,如果是同步操作,需要給這個(gè)參數(shù)賦值為NULL

異步操作方式

設(shè)置Overlapped參數(shù)實(shí)現(xiàn)同步

一般能夠異步操作的函數(shù)都設(shè)置一個(gè)OVERLAPPED類型的參數(shù),它的定義如下

typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; DWord Offset; DWORD OffsetHigh; HANDLE hEvent; } OVERLAPPED;

對于這個(gè)參數(shù)在使用時(shí),其余的我們都不需要關(guān)心,一般只使用最后一個(gè)hEvent成員,這個(gè)成員是一個(gè)事件對象的句柄,在使用時(shí),先創(chuàng)建一個(gè)事件對象,并設(shè)置事件對象無信號,并將句柄賦值給這個(gè)成員,一旦異步操作完成,那么系統(tǒng)會(huì)將這個(gè)事件設(shè)置為有信號,在需要同步的地方使用Wait系列的函數(shù)進(jìn)行等待即可。

int main(){ HANDLE hDevice = CreateFile("test.dat", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此處設(shè)置FILE_FLAG_OVERLAPPED NULL ); if (hDevice == INVALID_HANDLE_VALUE) { 使用完成函數(shù)來實(shí)現(xiàn)異步操作

異步函數(shù)是在異步操作完成時(shí)由操作系統(tǒng)調(diào)用的函數(shù),所以我們可以在需要同步的地方等待一個(gè)同步對象,然后在異步函數(shù)中將這個(gè)對象設(shè)置為有信號。 使用異步函數(shù)必須使用帶有Ex的設(shè)備操作函數(shù),像ReadFileEx,WriteFileEx等等,Ex系列的函數(shù)相比于不帶Ex的函數(shù)來說,多了最后一個(gè)參數(shù),LPOVERLAPPED_COMPLETION_ROUTINE 類型的回調(diào)函數(shù),這個(gè)函數(shù)的原型如下:

VOID CALLBACK FileIOCompletionRoutine( __in DWORD dwErrorCode, __in DWORD dwNumberOfBytesTransfered, __in LPOVERLAPPED lpOverlapped);

第一個(gè)參數(shù)是一個(gè)錯(cuò)誤碼,如果異步操作出錯(cuò),那么他的錯(cuò)誤碼可以由這個(gè)參數(shù)得到,第二個(gè)參數(shù)是實(shí)際操作的字節(jié)數(shù)對于Write類型的函數(shù)來說這個(gè)就是實(shí)際讀取的字節(jié)數(shù),第三個(gè)是一個(gè)異步對象。在使用這個(gè)方式進(jìn)行異步時(shí)Ex函數(shù)中的OVERLAPPED參數(shù)一般不需要為其設(shè)置事件句柄,只需傳入一個(gè)已經(jīng)清空的OVERLAPPED類型的內(nèi)存地址即可。 當(dāng)我們設(shè)置了該函數(shù)后,操作系統(tǒng)會(huì)將這個(gè)函數(shù)插入到相應(yīng)的隊(duì)列中,一旦完成這個(gè)異步操作,系統(tǒng)就會(huì)調(diào)用這個(gè)函數(shù),Windows中將這種機(jī)制叫做異步過程調(diào)用(APC Asynchronous Produre Call);這種機(jī)制也不是一定會(huì)執(zhí)行,一般只有程序進(jìn)入警戒狀態(tài)時(shí)才會(huì)執(zhí)行,想要程序進(jìn)入警戒狀態(tài)需要調(diào)用帶有Ex的等待函數(shù),包括SleepEx,在其中的bAlertable設(shè)置為TRUE那么當(dāng)其進(jìn)入等待狀態(tài)時(shí)就會(huì)調(diào)用APC隊(duì)列中的函數(shù),需要注意的是所謂的APC就是系統(tǒng)借當(dāng)前線程的線程環(huán)境來執(zhí)行我們提供的回調(diào)函數(shù),是用當(dāng)前線程環(huán)境模擬了一個(gè)輕量級的線程,這個(gè)線程沒有自己的線程上下文,所以在回調(diào)函數(shù)中不要進(jìn)行耗時(shí)的操作,否則一旦原始線程等到的它的執(zhí)行條件而被喚醒,而APC例程還沒有被執(zhí)行完成的話,就會(huì)造成一定的錯(cuò)誤。下面是使用這種方式進(jìn)行異步操作的例子:

VOID CALLBACK MyFileIOCompletionRoutine( DWORD dwErrorCode, // 對于此次操作返回的狀態(tài) DWORD dwNumberOfBytesTransfered, // 告訴已經(jīng)操作了多少字節(jié),也就是在IRP里的Infomation LPOVERLAPPED lpOverlapped // 這個(gè)數(shù)據(jù)結(jié)構(gòu)){ SetEvent(lpOverlapped->hEvent); printf("IO
Operation end!/n");}int main(){ HANDLE hDevice = CreateFile("test.dat", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此處設(shè)置FILE_FLAG_OVERLAPPED NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf("Read Error/n"); return 1; } UCHAR buffer[BUFFER_SIZE]; //初始化overlap使其內(nèi)部全部為零 //不用初始化事件!! OVERLAPPED overlap={0}; //這里沒有設(shè)置OVERLAP參數(shù),因此是異步操作 overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); ReadFileEx(hDevice, buffer, BUFFER_SIZE,&overlap,MyFileIOCompletionRoutine); //做一些其他操作,這些操作會(huì)與讀設(shè)備并行執(zhí)行 printf("在此處可以執(zhí)行其他操作/n"); //進(jìn)入alterable,只是為了有機(jī)會(huì)執(zhí)行APC函數(shù) SleepEx(1000, TRUE); //在此處進(jìn)行同步,只有當(dāng)讀操作完成才關(guān)閉句柄 WaitForSingleObject(overlap.hEvent, INFINITE); CloseHandle(hDevice); return 0;}

在最后SleepEx讓線程休眠而使其有機(jī)會(huì)執(zhí)行APC例程,然后使用WaitForSingleObject來等待事件,我們在APC例程中將事件置為有信號,這樣只有當(dāng)異步操作完成,才會(huì)返回,利用這個(gè)可以在關(guān)鍵的位置實(shí)現(xiàn)同步,在這里按理來說可以直接用WaitForSingleObjectEx來替換這兩個(gè)函數(shù)的調(diào)用,但是不知道為什么使用WaitForSingleObjectEx時(shí),即使我沒有設(shè)置為有信號的狀態(tài)它也能正常返回,所以為了體現(xiàn)這點(diǎn),我是使用了SleepEx和WaitForSingleObject兩個(gè)函數(shù)。

IRP中的同步和異步操作

上述的同步和異步操作必須得到內(nèi)核的支持,其實(shí)所有對設(shè)備的操作最終都會(huì)轉(zhuǎn)化為IRP請求,并傳遞到相應(yīng)的派遣函數(shù)中,在派遣函數(shù)中可以直接結(jié)束IRP,或者讓派遣函數(shù)返回,在以后的某個(gè)時(shí)候處理,由于應(yīng)用層會(huì)等待派遣函數(shù)返回,所以直接結(jié)束IRP的方式可以看做是同步,而先返回以后處理的方式可以看做是異步處理。 在CreateFile中沒有異步的方式,所以它會(huì)一直等待派遣函數(shù)調(diào)用IoCompleteRequest結(jié)束,所以當(dāng)調(diào)用CreateFile打開一個(gè)自己寫的設(shè)備時(shí)需要編寫一個(gè)用來處理IRP_MJ_CREATE的派遣函數(shù),并且需要在函數(shù)中結(jié)束IRP,否則CreateFile會(huì)報(bào)錯(cuò),之前本人曾經(jīng)犯過這樣的錯(cuò)誤,沒有為設(shè)備對象準(zhǔn)備IRP__MJ_CREATE的派遣函數(shù),結(jié)果CreateFile直接返回-1. 對于ReadFile和WriteFile來說,它們支持異步操作,在調(diào)用這兩個(gè)函數(shù)進(jìn)行同步操作時(shí),內(nèi)部會(huì)生成一個(gè)事件并等待這個(gè)事件,這個(gè)事件會(huì)和IRP一起發(fā)送的派遣函數(shù)中,當(dāng)IRP被結(jié)束時(shí),事件會(huì)被置為有信號,這樣函數(shù)中的等待就可以正常返回。而異步操作就不會(huì)產(chǎn)生這個(gè)事件。而是使用函數(shù)中的overlapped參數(shù),這時(shí)它內(nèi)部不會(huì)等待這個(gè)事件,而由程序員自己在合適的位置等待。 而調(diào)用帶有Ex的I/O函數(shù)則略有不同,他不會(huì)設(shè)置overlapped參數(shù)中的事件,而是當(dāng)進(jìn)入警告模式時(shí)調(diào)用提供的APC函數(shù)。 在派遣函數(shù)中可以調(diào)用IoCompleteRequest函數(shù)來結(jié)束IRP的處理或者調(diào)用IoMarkIrpPending來暫時(shí)掛起IRP,將IRP進(jìn)行異步處理。該函數(shù)原型如下:

VOID IoMarkIrpPending( IN OUT PIRP Irp );

下面的例子演示了如何進(jìn)行IRP的異步處理

typedef struct IRP_QUEUE_struct{ LIST_ENTRY IRPlist; PIRP pPendingIrp;}IRP_QUEUE, *LPIRP_QUEUE;NTSTATUS DefaultDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ){ NTSTATUS status; PIO_STACK_LOCATION pIrpStack; pIrpStack = IoGetCurrentIrpStackLocation(Irp); switch(pIrpStack->MajorFunction) { case IRP_MJ_READ: { PLIST_ENTRY pQueueHead; LPIRP_QUEUE pQueue; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_PENDING; pQueue = (LPIRP_QUEUE)ExAllocatePoolWithTag(PagedPool, sizeof(IRP_QUEUE), TAG); if(pQueue != NULL) { pQueue->pPendingIrp = Irp; pQueueHead = (PLIST_ENTRY)(DeviceObject->DeviceExtension); InsertHeadList(pQueueHead, &(pQueue->IRPlist)); } IoMarkIrpPending(Irp); return STATUS_PENDING; } break; case IRP_MJ_CLEANUP: { PLIST_ENTRY pQueueHead; LPIRP_QUEUE pQueue; PLIST_ENTRY pDelete; pQueueHead = (PLIST_ENTRY)(DeviceObject->DeviceExtension); if(NULL != pQueueHead) { while(!IsListEmpty(pQueueHead)) { pDelete = RemoveHeadList(pQueueHead); pQueue = CONTAINING_RECORD(pDelete, IRP_QUEUE, IRPlist); IoCompleteRequest(pQueue->pPendingIrp, IO_NO_INCREMENT); ExFreePoolWithTag(pQueue, TAG); pQueue = NULL; } } } default: { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } break; }}VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){ UNICODE_STRING uDeviceName; UNICODE_STRING uSymbolickName; UNREFERENCED_PARAMETER(DriverObject); RtlInitUnicodeString(&uDeviceName, DEVICE_NAME); RtlInitUnicodeString(&uSymbolickName, SYMBOLIC_NAME); IoDeleteSymbolicLink(&uSymbolickName); IoDeleteDevice(DriverObject->DeviceObject); DbgPrint("GoodBye World/n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){ NTSTATUS status; int i = 0; PDEVICE_OBJECT pDeviceObject; UNREFERENCED_PARAMETER(RegistryPath); DriverObject->DriverUnload = DriverUnload; status = CreateDevice(DriverObject, &pDeviceObject); for(i = 0; i < IRP_MJ_MAXIMUM_FUNCTION + 1; i++) { DriverObject->MajorFunction[i] = DefaultDispatch; } DbgPrint("Hello world/n"); return status;}NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject,PDEVICE_OBJECT *ppDeviceObject){ NTSTATUS status; PLIST_ENTRY pIrpQueue = NULL; UNICODE_STRING uDeviceName; UNICODE_STRING uSymbolickName; RtlInitUnicodeString(&uDeviceName, &DEVICE_NAME); RtlInitUnicodeString(&uSymbolickName, SYMBOLIC_NAME); if(NULL != ppDeviceObject) { status = IoCreateDevice(pDriverObject, sizeof(LIST_ENTRY), &uDeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, ppDeviceObject); if(!NT_SUCCESS(status)) { return status; } (*ppDeviceObject)->Flags |= DO_BUFFERED_IO; status = IoCreateSymbolicLink(&uSymbolickName, &uDeviceName); if(!NT_SUCCESS(status)) { IoDeleteDevice(*ppDeviceObject); *ppDeviceObject = NULL; return status; } pIrpQueue = (PLIST_ENTRY)((*ppDeviceObject)->DeviceExtension); InitializeListHead(pIrpQueue); return status; } return STATUS_UNSUCCESSFUL;}

在上述代碼中,定義一個(gè)鏈表用來保存未處理的IRP,然后在DriverEntry中創(chuàng)建一個(gè)設(shè)備對象,將鏈表頭指針放入到設(shè)備對象的擴(kuò)展中,在驅(qū)動(dòng)的IRP_MJ_READ請求中,將IRP保存到鏈表中,然后直接調(diào)用IoMarkIrpPending,將IRP掛起。一般的IRP_MJ_CLOSE用來關(guān)閉內(nèi)核創(chuàng)建的內(nèi)核對象,對應(yīng)用層來說也就是句柄,而IRP_MJ_CLEANUP用來處理被掛起的IRP,所以在這我們需要對CLEANUP的IRP進(jìn)行處理,在處理它時(shí),我們從鏈表中依次取出IRP,調(diào)用IoCompleteRequest直接結(jié)束并清除這個(gè)節(jié)點(diǎn)。對于其他類型的IRP則直接結(jié)束掉即可。 在應(yīng)用層,利用異步處理的方式多次調(diào)用ReadFile,最后再IrpTrace工具中可以看到,有多個(gè)顯示狀態(tài)位Pending的IRP。 在處理IRP時(shí)除了調(diào)用IoCompleteRequest結(jié)束之外還可以調(diào)用IoCancelIrp來取消IRP請求。這個(gè)函數(shù)原型如下:

BOOLEAN IoCancelIrp( IN PIRP Irp );

當(dāng)調(diào)用這個(gè)函數(shù)取消相關(guān)的IRP時(shí),對應(yīng)的取消例程將會(huì)被執(zhí)行,在DDK中可以使用函數(shù)IoSetCancelRoutine,該函數(shù)可以通過第二個(gè)參數(shù)為IRP設(shè)置一個(gè)取消例程,如果第二個(gè)參數(shù)為NULL,那么就將之前綁定到IRP上的取消例程給清除。函數(shù)原型如下:

PDRIVER_CANCEL IoSetCancelRoutine( IN PIRP Irp, IN PDRIVER_CANCEL CancelRoutine );

在調(diào)用IoCancelIrp函數(shù)時(shí)系統(tǒng)在內(nèi)部會(huì)獲取一個(gè)名為cancel的自旋鎖,然后進(jìn)行相關(guān)操作,但是自旋鎖的釋放需要自己來進(jìn)行,一般在取消例程中進(jìn)行釋放操作。這個(gè)自旋鎖可以通過函數(shù)IoAcquireCancelSpinLock來獲取,通過IoReleaseCancelSpinLock來釋放,下面是一個(gè)演示如何使用取消例程的例子。

//取消例程VOID CancelReadIrp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ){ Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); IoReleaseCancelSpinLock(Irp->CancelIrql);}//IRP_MJ_READ 處理case IRP_MJ_READ:{ Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_PENDING; IoSetCancelRoutine(Irp, CancelReadIrp); IoMarkIrpPending(Irp); return STATUS_PENDING;}

在R3層可以利用CancelIO,來使系統(tǒng)調(diào)用取消例程。 這個(gè)API傳入的是設(shè)備的句柄,當(dāng)調(diào)用它時(shí)所有針對該設(shè)備的被掛起的IRP都會(huì)調(diào)用對應(yīng)的取消例程,在這就不需要像上面那樣保存被掛起的IRP,每當(dāng)有READ請求過來時(shí)都會(huì)調(diào)用case里面的內(nèi)容,將該IRP和取消例程綁定,每當(dāng)有IRP被取消時(shí)都會(huì)調(diào)用對應(yīng)的取消例程,就不再需要自己維護(hù)了。另外在取消時(shí),系統(tǒng)會(huì)自己獲取這個(gè)cancel自旋鎖,并提升對應(yīng)的IRQL,IRP所處的IRQL被保存在IRP這個(gè)結(jié)構(gòu)的CancelIrql成員中,而調(diào)用IoReleaseCancelSpinLock函數(shù)釋放自旋鎖時(shí)需要的參數(shù)正是這個(gè)IRP對應(yīng)的IRQL,所以這里直接傳入Irp->CancelIrql


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 汉阴县| 荆州市| 新和县| 隆林| 扶沟县| 潮州市| 白城市| 卓资县| 博野县| 宣武区| 凯里市| 新郑市| 丰顺县| 安泽县| 五河县| 瑞丽市| 公安县| 五家渠市| 铜川市| 邹平县| 昌都县| 驻马店市| 富阳市| 靖远县| 万州区| 马尔康县| 平舆县| 西华县| 濮阳县| 政和县| 临猗县| 中西区| 探索| 新绛县| 班玛县| 东港市| 皮山县| 华池县| 德昌县| 杂多县| 诸城市|