前面我们谈到了关于异步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.
别以为要做好了,还有好多疑问:
- 如何叫底层驱动做我么要他做的事情呢(很明显这里不能用常规的函数,否则当前线程就会执行这个函数的功能)
- 刚才的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;
}
注意这里传入当前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
-------------------参考资料-----------------
- DPC定时器何时返回的问题 http://bbs.pediy.com/showthread.php?t=110344
- 内核中线程的创建与销毁 http://hi.baidu.com/sysinternal/blog/item/f2b877084535c532e92488cc.html
- 关于DeviceIoControl异步调用的笔记【1】:http://yexin218.iteye.com/blog/638445
最后感谢两个人: 南部天天以及古越魂
本文详细探讨了在Windows环境下通过DeviceIoControl实现异步I/O的过程。包括如何正确配置OVERLAPPED结构,底层驱动如何处理异步请求并返回PENDING状态,以及如何利用额外线程完成任务,并最终通知用户模式应用。
330

被折叠的 条评论
为什么被折叠?



