DeviceIoControl实现异步的方法总结
DeviceIoControl实现异步的方法总结
前面我们谈到了关于异步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;
- }
/**
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;
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.
别以为要做好了,还有好多疑问:
- 如何叫底层驱动做我么要他做的事情呢(很明显这里不能用常规的函数,否则当前线程就会执行这个函数的功能)
- 刚才的IRP请求到底执行结束没?
- 最后以何种方式告诉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;
- }
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);
- }
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);
- }
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))
//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
-------------------参考资料-----------------
- DPC定时器何时返回的问题http://bbs.pediy.com/showthread.php?t=110344

浙公网安备 33010602011771号