WDF队列分析(1)--序幕

    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开始分门别类的被处理,本篇完。下篇分析这些派生类对象被设置的时机。

猜你喜欢

转载自blog.csdn.net/lixiangminghate/article/details/80685059
wdf