關於DeviceIoControl實現異步的筆記【2】

前面我們談到了關於異步I/O的實現:關於DeviceIoControl實 現異步的筆記【1】 。可是實現起來,你會發現你的程序在DevieIoControl已經被掛起,而且返回的結果是非0。這就與真正的異步調用返回結果有出入,理論上應該返回0,且GetLastError()值爲ERROR_IO_PENDING。

/**
   Send the packets defined by users
*/
BOOL FilterWrapper::SendMyOwnPacket()
{
   BOOL result = FALSE;
   DWORD bytesWritten = 0;
   DWORD varEventResult;
   OVERLAPPED varOverLapped;
   HANDLE varObjectHandle = 0;
   LPVOID testBuffer = NULL; 

   PBYTE pBuf = NULL;
   DWORD testBufferLength = (DWORD)sizeof("Hi Mon, I finish your request!\n");  
   testBuffer =  new BYTE[testBufferLength];  
    if(testBuffer == NULL)  
    {  
	goto Exit;  
    } 
   
   varObjectHandle = CreateEvent(NULL,TRUE, TRUE,"");
   if(varObjectHandle == NULL)
      goto Exit;
   memset(&varOverLapped,0,sizeof(OVERLAPPED));
   varOverLapped.hEvent = varObjectHandle;
   varOverLapped.Offset = 0;
   varOverLapped.OffsetHigh = 0;
   
    // pass a new io control to underlying driver to send packets
	if(!DeviceIoControl(
                m_hFilter,
                IOCTL_FILTER_SEND_MYOWN_PACKET,
                "Request from user mode to Send A Packet.\n",
                sizeof("Request from user mode to Send A Packet.\n"),
                testBuffer,
                testBufferLength,
                &bytesWritten,
                (LPOVERLAPPED)&varOverLapped))
	{
		//printf("Can't Send Packet\n");
		if(GetLastError() != ERROR_IO_PENDING)
		{
	      printf("Overlapped I/O exception\n");
		  goto Exit;
        }else{
          printf("Overlappedn pending....\n");
       }		
	}
	printf("Son, I am calling you for dinner...\n");
	varEventResult = WaitForSingleObject(varObjectHandle,6000);
	switch(varEventResult)
	{
	  case WAIT_OBJECT_0 :
	       printf("overlapped i/0 workss\n");
		   pBuf = (PBYTE)testBuffer;
		   printf("Return buffer is %s\n",pBuf);
		   result = TRUE;
		   break;
	  case WAIT_TIMEOUT:
	       varEventResult = CancelIo(m_hFilter);
		    result = FALSE;
		   break;
	  default:
	       break;
	}
	// printf("Successfully Send A packet!^_^\n");
     ResetEvent(varObjectHandle);
	 CloseHandle(varObjectHandle);
Exit:
   delete[] testBuffer;
   return result;
}	

 所以每次都不會打印Overlappedn pending....這一句,因爲DeviceIoControl返回爲非零。我原本愚蠢的以爲,底層驅動是不需要更改就可以實現異步I/O。但是我錯了,從一開始我就錯了。那麼亡羊補牢吧。我們進行底層驅動的處理:

由於你要求驅動做的工作不能即時完成,所以我們先返回一個PENDING狀態:

case IOCTL_FILTER_SEND_MYOWN_PACKET:
	    InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
        InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
        OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
//這裏等下講如何叫底層驅動做該做的事情
//一個疑問在這裏:如果像常規的函數在這裏調用,那麼跟同步I/O有何差異?
//如果不這樣,有其他方法嗎?
DEBUGP(DL_TEST,("I am waiting this io dispath\n")); 
		Status = STATUS_PENDING;
        IoMarkIrpPending(Irp);
        Irp->IoStatus.Status = Status;
        return Status;
	break;

 這裏返回的狀態爲STATUS_PENDING,所以導致GetLastError值爲ERROR_IO_PENDING,而是用overlapped i/o的異步方式導致DeviceIoControl返回爲0.

別以爲要做好了,還有好多疑問:

 

  1. 如何叫底層驅動做我麼要他做的事情呢(很明顯這裏不能用常規的函數,否則當前線程就會執行這個函數的功能)
  2. 剛纔的IRP請求到底執行結束沒?
  3. 最後以何種方式告訴User層應用程序,某個時間已經是signaled狀態,然後讀取最後執行結果?

帶着這個三個問題,我們繼續講:

既然不能用常規的函數,我們想想有什麼方法可以讓這個函數獨立運行,而不受當前線程控制,答案就是在創建一個線程,負責該項工作。所以在上面的代碼中間添加:

		Status = PsCreateSystemThread(&threadHandle,
                                         THREAD_ALL_ACCESS,
                                         NULL,
                                         NULL,
                                         NULL,
                                        (PKSTART_ROUTINE) printSomething,
                                         Irp
                             );
		if( !NT_SUCCESS(Status))
         {
              DEBUGP(DL_TEST,("Fail to start a thread!\n"));
               return Status;
         }	

 注意這裏傳入當前IRP的指針。當該線程完成工作後,結束該IRP。

接下來看看線程調用printSomething這個函數:

VOID
printSomething(
   IN PIRP    pIrp
   ){
     PUCHAR    OutputBuffer = NULL;
	 PUCHAR    pTestBuf = "Hi Mon, I finish your request!\n";  
	 ULONG     bufSize = sizeof("Hi Mon, I finish your request!\n");
     mySleepTimer(5);
     DEBUGP(DL_TEST,("Five seconds,I have finished done something,hahhaha\n"));
     pIrp->IoStatus.Status = NDIS_STATUS_SUCCESS;
	 OutputBuffer = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
	 NdisMoveMemory(OutputBuffer,pTestBuf,bufSize);
	 pIrp->IoStatus.Information = bufSize;
     IoCompleteRequest(pIrp, IO_NO_INCREMENT);
	 PsTerminateSystemThread(STATUS_SUCCESS);
   }
 

這裏,我們等待5秒鐘,然後返回。返回前設置輸出緩衝區的數據,返回給user,其次設置返回的狀態Success等。最後調用IoCompleteRequest()函數通知User中的Event事件,把Event設置成Signaled狀態,使得WaitForSignalObject函數可以繼續執行。

這樣才完成異步I/O的調用,其實自己細看,使用同步時,DeviceIoControl被掛起,現在使用異步,DeviceIoControl立刻返回,但是在WaitForSignalObject中掛起等待Event的狀態改變。所以要真正實現異步,估計還需要在User層使用線程,用線程負責該DeviceIoControl的調用。才能真正意義上實現異步。

----------------------------------------附上MySleepTimer()------------------------------

這個函數實現的功能是延遲5秒鐘。

VOID 
mySleepTimer(
   IN ULONG time
){
   LARGE_INTEGER my_interval;
   my_interval.QuadPart = RELATIVE(SECONDS(5));
   KeDelayExecutionThread(KernelMode,FALSE,&my_interval);
}

 關鍵是在SECONDS()的宏定義,來自Osronline的牛人寫的:

//Define some times
#define ABSOLUTE(wait) (wait)

#define RELATIVE(wait) (-(wait))

#define NANOSECONDS(nanos) \
(((signed __int64)(nanos)) / 100L)

#define MICROSECONDS(micros) \
(((signed __int64)(micros)) * NANOSECONDS(1000L))

#define MILLISECONDS(milli) \
(((signed __int64)(milli)) * MICROSECONDS(1000L))

#define SECONDS(seconds) \
(((signed __int64)(seconds)) * MILLISECONDS(1000L))

 所以等相對的5秒鐘就是 RELATIVE(SECONDS(5)),很強大~

------------------------------------附上圖片---------------------------------

執行過程中,WaitForsignalObject被掛起:



 最後執行完成:


下面是Debugview信息:

0005056    261.43447876    NDISLWF:    
00005057    261.43450928    The input length is 42, and inputdata is Request from user mode to Send A Packet.    
00005058    261.43450928        
00005059    261.43460083    NDISLWF:    
00005060    261.43460083    I am waiting this io dispath   

.......

00005229    266.43710327    NDISLWF:    
00005230    266.43713379    Five seconds,I have finished done something,hahhaha 

-------------------參考資料-----------------

  1. DPC定時器何時返回的問題 http://bbs.pediy.com/showthread.php?t=110344
  2. 內核中線程的創建與銷燬 http://hi.baidu.com/sysinternal/blog/item/f2b877084535c532e92488cc.html
  3. 關於DeviceIoControl異步調用的筆記【1】:http://yexin218.iteye.com/blog/638445

最後感謝兩個人: 南部天天以及古越魂

發佈了53 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章