• 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

  • 相关阅读:
    Solr相似度算法二:BM25Similarity
    Solr相似度算法一:Lucene TF-IDF 相关性算分公式
    Solr相似度算法一:DefaultSimilarity(基于TF-IDF的默认相似度算法)
    Solr特性:Schemaless Mode(自动往Schema中添加field)
    Mahout的taste里的几种相似度计算方法
    Solr之functionQuery(函数查询)
    Solr中的一些查询参数
    solr特点三: 基于Solr实现排序定制化参考
    solr特点三: 排序样例汇总
    Solr 使用自定义 Query Parser(短语查询,精准查询)
  • 原文地址:https://www.cnblogs.com/kevinzhwl/p/3878878.html
Copyright © 2020-2023  润新知