WDF框架中IoQueue的实现相当复杂,覆盖的内容很广,需要大量的篇幅来分析。至于demo程序,还是以我们熟悉的toaster入手。虽然IoQueue的实现分散在WDF源码的很多角落,但我还是整理出一个线索,这几篇文章会围绕着它逐步展开。好,现在来看下所谓的线索:
kd> g
Breakpoint 0 hit
wdfsimple!ToasterEvtIoRead+0x39:
a64b41b9 8b5508 mov edx,dword ptr [ebp+8]
kd> kb
ChildEBP RetAddr Args to Child
acd13a00 86715c84 368477c8 4fbd0228 0000000a wdfsimple!ToasterEvtIoRead+0x39 [c:\winddk\7600.16385.1\src\general\toaster\kmdf\func\simple\toaster.c @ 306]
acd13a1c 8674c82a 368477c8 4fbd0228 0000000a Wdf01000!FxIoQueueIoStop::Invoke+0x2e [minkernel\wdf\framework\shared\inc\private\common\fxioqueuecallbacks.hpp @ 159]
acd13a54 86712b91 0000000a b042fdd0 c97b8830 Wdf01000!FxIoQueue::DispatchRequestToDriver+0x39a0a [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 3272]
acd13a78 867133df acd13a00 00000000 af9b88f0 Wdf01000!FxIoQueue::DispatchEvents+0x241 [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 3125]
acd13a98 86711bb6 b042fdd0 8ac98244 8d656020 Wdf01000!FxIoQueue::QueueRequest+0x7f [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 2371]
acd13af0 821584b8 01656020 00647374 c96472e0 Wdf01000!FxDevice::DispatchWithLock+0x316 [minkernel\wdf\framework\shared\core\fxdevice.cpp @ 1430]
acd13b0c 8239ce2c c9647398 c96472e0 8d656020 nt!IofCallDriver+0x48
这是toaster处理IRP_MJ_READ时的调用堆栈,请不要因为他是所谓的线索而感到意外,我也是顺着这个调用栈,慢慢梳理出IoQueue的轮廓。所以,请你耐心的往下看下去。
如果熟悉wdm驱动,那你一定知道调用IoCallDriver后,会调用对应驱动的Dispatch函数。根据上面堆栈的最后两行输出,可知Toaster处理IRP_MJ_READ的Dispatch函数是FxDevice::DispatchWithLock。是不是有点震惊?看toaster\kmdf\func\simple\toaster.c的代码,MS像是把Dispatch函数设置为:
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
queueConfig.EvtIoRead = ToasterEvtIoRead;
queueConfig.EvtIoWrite = ToasterEvtIoWrite;
queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl;
看来一定是FxDevice::DispatchWithLock封装并调用了ToasterEvtIoRead。所以现在要做的是确定IRP_MJ_READ回调函数确实是FxDevice::DispatchWithLock(具体步骤请移步我的另一篇博客):
kd> !drvobj 8e54baf8 7
Driver object (8e54baf8) is for:
\Driver\wdffeatured
Driver Extension List: (id , addr)
(862ecd8a a4a06068)
Device Object list:
b0a90390
DriverEntry: a82224e0 wdffeatured!FxDriverEntry
DriverStartIo: 00000000
DriverUnload: a82225cc wdffeatured!FxStubDriverUnload
AddDevice: 862b09de Wdf01000!FxDriver::AddDevice
Dispatch routines:
[00] IRP_MJ_CREATE 862918a0 Wdf01000!FxDevice::DispatchWithLock
[02] IRP_MJ_CLOSE 862918a0 Wdf01000!FxDevice::DispatchWithLock
[03] IRP_MJ_READ 862918a0 Wdf01000!FxDevice::DispatchWithLock
[04] IRP_MJ_WRITE 862918a0 Wdf01000!FxDevice::DispatchWithLock
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 862918a0 Wdf01000!FxDevice::DispatchWithLock
[10] IRP_MJ_SHUTDOWN 862918a0 Wdf01000!FxDevice::DispatchWithLock
[12] IRP_MJ_CLEANUP 862918a0 Wdf01000!FxDevice::DispatchWithLock
[16] IRP_MJ_POWER 862918a0 Wdf01000!FxDevice::DispatchWithLock
[17] IRP_MJ_SYSTEM_CONTROL 862918a0 Wdf01000!FxDevice::DispatchWithLock
[1b] IRP_MJ_PNP 862918a0 Wdf01000!FxDevice::DispatchWithLock
参windbg的输出所示,WDF驱动的IRP处理函数清一色被设置成FxDevice::DispatchWithLock!起初我还以为调试器出了问题,反复核对源码才确定这既是事实!已知IRP处理函数是FxDevice::DispatchWithLock,只要查找它的所有引用即可知道在哪设置了这些派遣函数。但是,先不要急着查找引用,反问自己一个问题:在wdm中,一般什么时候设置驱动派遣函数?答案是在DriverEntry返回前。以此类推,wdf驱动也做了同样的事。在wdf中DriverEntry主要是调用WdfCreateDriver,因此,我猜测为驱动程序设置回调函数是由WdfCreateDriver完成。我们可以在FxDriver::Initialize中找到这样的代码:
WdfDriverCreate(...)
{
...
pDriver = new(pFxDriverGlobals, DriverAttributes)
FxDriver(DriverObject, DriverConfig, pFxDriverGlobals);
if (pDriver != NULL) {
if (NT_SUCCESS(status)) {
status = pDriver->Initialize(RegistryPath, DriverConfig, DriverAttributes);
...
}
在FxDriver::Initialize中,驱动程序的派遣函数在循环内被统一的设置成Wdf01000!FxDevice::DispatchWithLock,如下:
FxDriver::Initialize(
__in PCUNICODE_STRING ArgRegistryPath,
__in PWDF_DRIVER_CONFIG Config,
__in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
)
{
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
m_DriverObject.SetMajorFunction(i, FxDevice::Dispatch);
}
else {
m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLock);
}
}
嗯,感觉DispatchWithLock十有八九是个代理函数:根据不同类型的IRP,做不同的处理。再继续分析之前,容我啰嗦一下DispatchWithLock这个函数名中的Lock的含义:它指设备对象扩展域中的自定义的RemoveLock。在WDF框架中,处理PNP/Power这类IRP前需要获取RemoveLock,而处理Create/Close/Cleanup以及Read/Write/ioctl这类IRP没有强制需要获得RemoveLock。关于RemoveLock的细节,参考win7 ddk的event样例。
回到正题,DispatchWithLock在判断和获取RemoveLock后,先后进入FxDevice::Dispatch和DispatchWorker。DispatchWorker将引出本篇的主角----IoQueue:
__inline
NTSTATUS
DispatchWorker(
__in FxDevice* Device,
__in MdIrp Irp,
__in WDFCONTEXT DispatchContext
)
{
...
return Device->GetDispatchPackage(
irp.GetMajorFunction()
)->Dispatch(Irp);
}
GetDispatchPackage返回FxPackage*基类指针,Dispatch是类FxPackage中的虚函数。很明显,MS是想通过多态实现对IRP多元化处理。目前WDF中有4种FxPackage的派生类满足4种处理方式:
1).IRP_MJ_Create/Close/Cleanup/Shutdown对应的派生类为FxPkgGeneral;
2).IRP_MJ_Read/Write/DeviceIoControl对应的派生类为FxPkgIo;
3).IRP_MJ_Pnp/Power对应的派生类为FxPkgPnp;
4).其他类型的IRP对应的派生类为FxDefaultIrpHandler。
这些派生类各自实现了各自的Dispatch函数,自此,IRP开始分门别类的被处理,本篇完。下篇分析这些派生类对象被设置的时机。