关于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被挂起:


关于DeviceIoControl实现异步的笔记【2】
 最后执行完成:


关于DeviceIoControl实现异步的笔记【2】

下面是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

最后感谢两个人: 南部天天以及古越魂

你可能感兴趣的:(thread,工作,PHP,Access,bbs)