WDF队列分析(3)

    《WDF队列分析(2)----IoQueue的创建》属于未完成篇,这篇接着往下分析。toaster是功能驱动,所以WDF框架顺着FxDevice::_Create函数,进入case FxDeviceInitTypeFdo分支:

case FxDeviceInitTypeFdo:
        status = pDevice->FdoInitialize(pInit);
        break;

熟悉wdm驱动的读者都清楚驱动程序响应IRP_MN_START事件后的三板斧:

1).进入AddDevice函数; 2).创建Fdo; 3).将Fdo挂载到设备栈上,如下所示:

NTSTATUS
ToasterAddDevice(
    __in PDRIVER_OBJECT DriverObject,
    __in PDEVICE_OBJECT PhysicalDeviceObject
    )
{
    status = IoCreateDevice (DriverObject,
                             sizeof (FDO_DATA),
                             NULL,
                             FILE_DEVICE_UNKNOWN,
                             FILE_DEVICE_SECURE_OPEN,
                             FALSE,
                             &deviceObject);
    fdoData->NextLowerDriver = IoAttachDeviceToDeviceStack (deviceObject,
                                                       PhysicalDeviceObject);

WDF框架也没有跳出这套框架,所以FdoInitialize仍然一板一眼的遵循这个流程:

1).设置Fdo所要挂载的Pdo:

m_PhysicalDevice.SetObject((MdDeviceObject)DeviceInit->Fdo.PhysicalDevice);

在wdm驱动中,我们创建设备对象的同时也为设备对象分配了设备扩展。设备扩展是片自定义的内存空间,自由度很大,但是很多驱动程序都会把Pdo保存到设备扩展中。但是,WDF框架并没有这么做:它很取巧的用到了FxDevice是DeviceObject的扩展这一特性,在类FxDevice中引入m_PhysicalDevice域,用它来保存Pdo。对于我们而言,Pdo信息放在哪并不重要,只要能解不时之需就行了。另外,你可能会对上面的代码片感到疑惑:代码行中DeviceInit出自何处?虽然我们身处FdoInitialize函数中,但不要忘了,这个函数是由WdfDeviceCreate间接调用的。DeviceInit作为WdfDeviceCreate的参数被一层层传递下来。而WdfDeviceCreate又由

NTSTATUS
ToasterEvtDeviceAdd(
    IN WDFDRIVER       Driver,
    IN PWDFDEVICE_INIT DeviceInit
    );

调用,所以DeviceInit来自于EvtDriverDeviceAdd传入的参数。

2).完备驱动程序IRP处理能力;

WDF队列分析(2)----IoQueue的创建》中说过,在调用FdoInitialize之前,所有FxDevice已经具有不完整的IRP处理能力。个性化定制由FdoInitialize实现,它执行下列语句,定制适用于Fdo的Pnp/Power处理方式:

status = FxPkgFdo::_Create(pGlobals, (CfxDevice*)this, &pkgFdo);

    if (!NT_SUCCESS(status)) {
        return status;
    }
    else {
        m_PkgPnp = pkgFdo;
    }

前面说过m_PkgPnp是FxDevice的类成员,用于处理IRP_MJ_Pnp/Power事件。既然代码中将m_PkgPnp设置为新创建的对象pkgFdo,那么pkgFdo势必也具有处理这些事件的能力。查找类定义,pkgFdo果然是FxPkgPnp的派生类:

class FxPkgFdo : public FxPkgPnp
FxPkgFdo::_Create函数主要设置处理PNP/POWER/PowerPolicy消息的队列,目前,这三者还不是我们的关注点,所以暂时不展开。等后面有机会再行深入。现阶段,我们只要知道当功能驱动调用GetDispatchPackage函数时,返回的真实对象类型是pkgFdo即可。从FxPkgFdo::_Create函数返回后,wdf准备创建DeviceOject。

3).创建DeviceOject并将设备加入到设备栈:

扫描二维码关注公众号,回复: 2368990 查看本文章
    status = CreateDevice(DeviceInit);
    if (NT_SUCCESS(status)) {
        m_AttachedDevice.SetObject(Mx::MxAttachDeviceToDeviceStack(
            m_DeviceObject.GetObject(), m_PhysicalDevice.GetObject()));

CreateDevice函数封装了wdm时代调用IoCreateDevice前做的通用设置,没必要展开讨论。

    做完以上步骤后,新创建的DeviceObject对象可以开始它的生命之旅。如果让我在它的生命之旅中挑选2个重要的时刻进行分析,我会选IRP_MJ_CREATE和IRP_MJ_READ,因为他们分别代表了两种典型的Package:FxPkgGeneral以及FxPkgIo。首先看下WDF处理IRP_MJ_CREATE时,在windbg中留下的脚印:

kd> x wdf01000!*OnCreate*
8727cb38 Wdf01000!imp_WdfDmaTransactionCreate (void)
872b2128 Wdf01000!imp_VfWdfCollectionCreate (void)
87248b84 Wdf01000!imp_WdfCollectionCreate (void)
8723afc0 Wdf01000!FxPkgGeneral::OnCreate (void)
872b25dc Wdf01000!imp_VfWdfDmaTransactionCreate (void)
kd> bp Wdf01000!FxPkgGeneral::OnCreate 
kd> g
Breakpoint 6 hit
Wdf01000!FxPkgGeneral::OnCreate:
8723afc0 8bff            mov     edi,edi
kd> kb
ChildEBP RetAddr  Args to Child              
#0 a81d7850 87231a40 a81d788c 8f6c0740 8ba0e030 Wdf01000!FxPkgGeneral::OnCreate [minkernel\wdf\framework\shared\irphandlers\general\fxpkggeneral.cpp @ 846]
#1 a81d78a8 81d534b8 00a0e030 8f6f2c38 00000000 Wdf01000!FxDevice::DispatchWithLock+0x1a0 [minkernel\wdf\framework\shared\core\fxdevice.cpp @ 1430]
#2 a81d78c0 81d5359f 8f6c0740 8cd77e58 a81d78e8 nt!IofCallDriver+0x48
a81d78e8 81f95e55 9d5c0705 8ba0e018 81f95130 nt!IoCallDriverWithTracing+0x2f
a81d799c 81f8a42a 8ba0e030 86445830 ad7b4008 nt!IopParseDevice+0xd25
a81d7aa4 81f87f58 00000040 86445830 8ba9ca01 nt!ObpLookupObjectName+0x3da
a81d7b2c 81f649e3 0149f4fc 86445830 8ba9ca01 nt!ObOpenObjectByNameEx+0x118
a81d7bb4 81f646c0 0149f4fc 0149f514 00000000 nt!IopCreateFile+0x313
a81d7bf4 81db6b87 0149f4e0 00100080 0149f4fc nt!NtOpenFile+0x30
a81d7bf4 77701670 0149f4e0 00100080 0149f4fc nt!KiSystemServicePostCall
WARNING: Stack unwind information not available. Following frames may be wrong.
0149f964 6bc437cf 0149f9b0 005274e8 0101fa48 ntdll+0x91670

先不解释我在Wdf01000!FxPkgGeneral::OnCreate处下断点的原因,回头一定会提到。从函数调用堆栈可以很清晰的看出OS收到应用层的OpenFile API后进入了某个基于WDF框架的驱动派遣函数。大家仔细看栈帧#1和#2,是不是有种似曾相识的感觉?你没记错,在《WDF队列分析(1)--序幕》中WDF框架响应IRP_MJ_READ消息时出现过:


只不过这是处理IRP_MJ_READ的调用堆栈。大家仔细对比蓝笔圈的栈帧和IRP_MJ_CREATE中的#1/#2栈帧,两者内容一样!还记得我曾说过基于WDF框架的驱动,它们的派遣函数无一例外的都被设置成Wdf01000!FxDevice::DispatchWithLock? DispatchWithLock算一个中转函数,根据IRP类型分别调用不同的Pkg->Dispatch函数。像IRP_MJ_CREATE这类IRP,对应的Pkg是FxPkgGeneral,它的Dispatch函数被设置为:

_Must_inspect_result_
NTSTATUS
FxPkgGeneral::Dispatch(
    __inout MdIrp Irp
    )
{
    ...
    switch (fxIrp.GetMajorFunction()) {
    case IRP_MJ_CREATE:
        status = OnCreate(&fxIrp);
        break;

OnCreate的实现不是很复杂,由于篇幅限制就不详细分析。顺带一提的是,在默认情况下WDF框架虽然直接结束IRP_MJ_CREATE等消息,但是还是可以在创建设备对象前为这类IRP设置回调函数:

_Must_inspect_result_
NTSTATUS
FxDevice::CreateDevice(
    __in PWDFDEVICE_INIT DeviceInit
    )
{
    status = m_PkgGeneral->Initialize(DeviceInit);

FxPkgGeneral::Initialize将为DeivceInit中自定义的关于Create/Close/Cleanup的回调函数创建一个节点,并加入到链表中:

NTSTATUS
FxPkgGeneral::Initialize(
    __in PWDFDEVICE_INIT DeviceInit
    )
{
    NTSTATUS                    status;
    PLIST_ENTRY                 next;
    FxFileObjectInfo*           fileObjInfo;
    PFX_DRIVER_GLOBALS          fxDriverGlobals;

    fxDriverGlobals = GetDriverGlobals();

    //
    // Init file object info.
    //
    if (DeviceInit->FileObject.Set) {
        fileObjInfo = new(fxDriverGlobals) FxFileObjectInfo();
        if (fileObjInfo == NULL) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            DoTraceLevelMessage(fxDriverGlobals, 
                                TRACE_LEVEL_ERROR, TRACINGDEVICE,
                                "Couldn't create object FileObjectInfo, "
                                "%!STATUS!", status);
            goto Done;
        }
        
        fileObjInfo->ClassExtension = FALSE;        
        fileObjInfo->FileObjectClass = DeviceInit->FileObject.Class;
        fileObjInfo->Attributes = DeviceInit->FileObject.Attributes;

        fileObjInfo->AutoForwardCleanupClose = 
            DeviceInit->FileObject.AutoForwardCleanupClose;
        
        fileObjInfo->EvtFileCreate.Method = 
            DeviceInit->FileObject.Callbacks.EvtDeviceFileCreate;
        
        fileObjInfo->EvtFileCleanup.Method = 
            DeviceInit->FileObject.Callbacks.EvtFileCleanup;
        
        fileObjInfo->EvtFileClose.Method = 
            DeviceInit->FileObject.Callbacks.EvtFileClose;

        InsertTailList(&m_FileObjectInfoHeadList, &fileObjInfo->ListEntry);

前面我在OnCreate符号上下断点并进行跟踪,也是我以m_FileObjectInfoHeadList为关键词,搜索到的调用结果。

当FxDevice进入工作状态,从而进入FxPkgGeneral::Dispatch后,将会从m_FileObjectInfoHeadList链表中取出并调用自定义的回调函数。相对而言,FxPkgGeneral的处理还是比较简单,至于IRP_MJ_READ消息的处理,我要放到下一篇了。

猜你喜欢

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