DeviceIoControl实现异步的方法总结

DeviceIoControl实现异步的方法总结

前面我们谈到了关于异步I/O的实现:关于DeviceIoControl实现异步的笔记【1】。可是实现起来,你会发现你的程序在DevieIoControl已经被挂起,而且返回的结果是非0。这就与真正的异步调用返回结果有出入,理论上应该返回0,且GetLastError()值为ERROR_IO_PENDING。

C代码复制代码
  1. /** 
  2.    Send the packets defined by users 
  3. */  
  4. BOOL FilterWrapper::SendMyOwnPacket()   
  5. {   
  6.    BOOL result = FALSE;   
  7.    DWORD bytesWritten = 0;   
  8.    DWORD varEventResult;   
  9.    OVERLAPPED varOverLapped;   
  10.    HANDLE varObjectHandle = 0;   
  11.    LPVOID testBuffer = NULL;    
  12.   
  13.    PBYTE pBuf = NULL;   
  14.    DWORD testBufferLength = (DWORD)sizeof("Hi Mon, I finish your request!/n");     
  15.    testBuffer =  new BYTE[testBufferLength];     
  16.     if(testBuffer == NULL)     
  17.     {     
  18.     goto Exit;     
  19.     }    
  20.       
  21.    varObjectHandle = CreateEvent(NULL,TRUE, TRUE,"");   
  22.    if(varObjectHandle == NULL)   
  23.       goto Exit;   
  24.    memset(&varOverLapped,0,sizeof(OVERLAPPED));   
  25.    varOverLapped.hEvent = varObjectHandle;   
  26.    varOverLapped.Offset = 0;   
  27.    varOverLapped.OffsetHigh = 0;   
  28.       
  29.     // pass a new io control to underlying driver to send packets  
  30.     if(!DeviceIoControl(   
  31.                 m_hFilter,   
  32.                 IOCTL_FILTER_SEND_MYOWN_PACKET,   
  33.                 "Request from user mode to Send A Packet./n",   
  34.                 sizeof("Request from user mode to Send A Packet./n"),   
  35.                 testBuffer,   
  36.                 testBufferLength,   
  37.                 &bytesWritten,   
  38.                 (LPOVERLAPPED)&varOverLapped))   
  39.     {   
  40.         //printf("Can't Send Packet/n");  
  41.         if(GetLastError() != ERROR_IO_PENDING)   
  42.         {   
  43.           printf("Overlapped I/O exception/n");   
  44.           goto Exit;   
  45.         }else{   
  46.           printf("Overlappedn pending..../n");   
  47.        }           
  48.     }   
  49.     printf("Son, I am calling you for dinner.../n");   
  50.     varEventResult = WaitForSingleObject(varObjectHandle,6000);   
  51.     switch(varEventResult)   
  52.     {   
  53.       case WAIT_OBJECT_0 :   
  54.            printf("overlapped i/0 workss/n");   
  55.            pBuf = (PBYTE)testBuffer;   
  56.            printf("Return buffer is %s/n",pBuf);   
  57.            result = TRUE;   
  58.            break;   
  59.       case WAIT_TIMEOUT:   
  60.            varEventResult = CancelIo(m_hFilter);   
  61.             result = FALSE;   
  62.            break;   
  63.       default:   
  64.            break;   
  65.     }   
  66.     // printf("Successfully Send A packet!^_^/n");  
  67.      ResetEvent(varObjectHandle);   
  68.      CloseHandle(varObjectHandle);   
  69. Exit:   
  70.    delete[] testBuffer;   
  71.    return result;   
  72. }     
/**
   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状态:

C代码复制代码
  1. case IOCTL_FILTER_SEND_MYOWN_PACKET:   
  2.         InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;   
  3.         InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;   
  4.         OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;   
  5. //这里等下讲如何叫底层驱动做该做的事情  
  6. //一个疑问在这里:如果像常规的函数在这里调用,那么跟同步I/O有何差异?  
  7. //如果不这样,有其他方法吗?  
  8. DEBUGP(DL_TEST,("I am waiting this io dispath/n"));    
  9.         Status = STATUS_PENDING;   
  10.         IoMarkIrpPending(Irp);   
  11.         Irp->IoStatus.Status = Status;   
  12.         return Status;   
  13.     break;  
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状态,然后读取最后执行结果?

带着这个三个问题,我们继续讲:

既然不能用常规的函数,我们想想有什么方法可以让这个函数独立运行,而不受当前线程控制,答案就是在创建一个线程,负责该项工作。所以在上面的代码中间添加:

C代码复制代码
  1. Status = PsCreateSystemThread(&threadHandle,   
  2.                                        THREAD_ALL_ACCESS,   
  3.                                        NULL,   
  4.                                        NULL,   
  5.                                        NULL,   
  6.                                       (PKSTART_ROUTINE) printSomething,   
  7.                                        Irp   
  8.                            );   
  9. if( !NT_SUCCESS(Status))   
  10.        {   
  11.             DEBUGP(DL_TEST,("Fail to start a thread!/n"));   
  12.              return Status;   
  13.        }      
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这个函数:

Cpp代码复制代码
  1. VOID  
  2. printSomething(   
  3.    IN PIRP    pIrp   
  4.    ){   
  5.      PUCHAR    OutputBuffer = NULL;   
  6.      PUCHAR    pTestBuf = "Hi Mon, I finish your request!/n";     
  7.      ULONG     bufSize = sizeof("Hi Mon, I finish your request!/n");   
  8.      mySleepTimer(5);   
  9.      DEBUGP(DL_TEST,("Five seconds,I have finished done something,hahhaha/n"));   
  10.      pIrp->IoStatus.Status = NDIS_STATUS_SUCCESS;   
  11.      OutputBuffer = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;   
  12.      NdisMoveMemory(OutputBuffer,pTestBuf,bufSize);   
  13.      pIrp->IoStatus.Information = bufSize;   
  14.      IoCompleteRequest(pIrp, IO_NO_INCREMENT);   
  15.      PsTerminateSystemThread(STATUS_SUCCESS);   
  16.    }  
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秒钟。

C代码复制代码
  1. VOID    
  2. mySleepTimer(   
  3.    IN ULONG time   
  4. ){   
  5.    LARGE_INTEGER my_interval;   
  6.    my_interval.QuadPart = RELATIVE(SECONDS(5));   
  7.    KeDelayExecutionThread(KernelMode,FALSE,&my_interval);   
  8. }  
VOID 
mySleepTimer(
   IN ULONG time
){
   LARGE_INTEGER my_interval;
   my_interval.QuadPart = RELATIVE(SECONDS(5));
   KeDelayExecutionThread(KernelMode,FALSE,&my_interval);
}

 关键是在SECONDS()的宏定义,来自Osronline的牛人写的:

Cpp代码复制代码
  1. //Define some times  
  2. #define ABSOLUTE(wait) (wait)  
  3.   
  4. #define RELATIVE(wait) (-(wait))  
  5.   
  6. #define NANOSECONDS(nanos) /  
  7. (((signed __int64)(nanos)) / 100L)   
  8.   
  9. #define MICROSECONDS(micros) /  
  10. (((signed __int64)(micros)) * NANOSECONDS(1000L))   
  11.   
  12. #define MILLISECONDS(milli) /  
  13. (((signed __int64)(milli)) * MICROSECONDS(1000L))   
  14.   
  15. #define SECONDS(seconds) /  
  16. (((signed __int64)(seconds)) * MILLISECONDS(1000L))  
//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

原文地址:https://www.cnblogs.com/kevinzhwl/p/3878878.html