Wdf框架之WdfObject状态机(2) 一文再补充

    万万没想到<Wdf框架之WdfObject状态机(2) >的内容如此之多,2篇博客的篇幅还不够承载,需要第三篇来完成最后一击。本文将进入FxObject::DeleteWorkerAndUnlock的else分支,分析DrvDestroyCallback/DrvCleanupCallback的调用时机。

    进入else分支后,首先遇到的函数是FxObject::DisposeChildrenWorker函数,这个函数用来清空所有隶属于本FxObject的子对象,因为kmdfdrv驱动是个空驱动,不存在子对象,所以将跳过两个for循环,直接进入DisposeChildrenWorker尾部的CallCleanup()语句:

kd> kb ;中断在Unload函数,观察句柄drvObj对应的FxObject信息
ChildEBP RetAddr  Args to Child              
8afabba0 8ad7c2de 5997b8a8 c1763a40 c1763a40 KMDFdrv!Unload+0x4 [g:\kmdfdrv\kmdfdrv\kmdf.c @ 15]
...
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> !wdfobject 0xa6684750
The type for object 0xa6684750 is FxDriver ;<-------drvObj对应的FxDriver对象的地址
State: FxObjectStateCreated (0x1)
!wdfhandle 0x5997b8a8
dt FxDriver 0xa6684750
...
kd> dt wdf01000!FxDriver 0xa6684750  -y m_ChildListHead ;m_ChildListHead是子对象列表,查看列表中的对象
   +0x014 m_ChildListHead : _LIST_ENTRY [ 0xa6684764 - 0xa6684764 ] ;FLink和BLink都指向列表头,所以子对象列表是个空列表
BOOLEAN
FxObject::DisposeChildrenWorker(FxObjectState NewDeferedState,KIRQL OldIrql,BOOLEAN CanDefer)
{
	...
    if ((m_ObjectFlags & FXOBJECT_FLAGS_DISPOSE_OVERRIDE) == 0x00 || Dispose()) {
        CallCleanup();
    }
}

CallCleanup调用FxObject::CallCleanupCallbacks,并最终进入KMDFdrv!EvtCleanupCallback函数:

VOID FxObject::CallCleanupCallbacks(VOID)
{
    FxContextHeader* pHeader;
    WDFOBJECT h;

    if (IsCommitted() == FALSE) {
        return;
    }

    h = GetObjectHandle();

    for (pHeader = GetContextHeader();
         pHeader != NULL;
         pHeader = pHeader->NextHeader) {
        if (pHeader->EvtCleanupCallback != NULL) {
            pHeader->EvtCleanupCallback(h);
            pHeader->EvtCleanupCallback = NULL;
        }
    }

    m_ObjectFlags &= ~FXOBJECT_FLAGS_HAS_CLEANUP;
}

FxObject::CallCleanupCallbacks的实现比较简单,因为EvtCleanupCallback存放在FxObject的扩展部分(!wdfobject扩展命令也是把EvtCleanupCallback和EvtDestroyCallback放在FxObject中一起显示),所以代码中首先获得FxObject扩展部分,并调用EvtCleanupCallback,最后把EvtCleanupCallback置空。(FxObject扩展的相关内容以后我会单独分析)。

    结合源码,可以发现,调用EvtCleanupCallback时,只有FxObject!m_ObjectState发生改变,至于引用计数,它还没有发生改变,保持进入Unload函数前的值。

kd> kb ;进入EvtCleanupCallback回调时的windbg输出
ChildEBP RetAddr  Args to Child              
8afabb70 8ad4b9c5 5997b8a8 a6684750 a6684764 KMDFdrv!DrvCleanupCallback [g:\kmdfdrv\kmdfdrv\kmdf.c @ 26]
8afabb84 8ad5119a b8b14bc8 a6684750 c1763a40 Wdf01000!FxObject::CallCleanupCallbacks+0x35 [minkernel\wdf\framework\shared\object\fxobject.cpp @ 354]
8afabba8 8ad7c310 c1763a40 8f436040 8afabbc4 Wdf01000!FxObject::DeleteObject+0x24dba [minkernel\wdf\framework\shared\object\fxobjectstatemachine.cpp @ 124]
8afabbb8 ab0b1324 c1f0c5a8 8afabbe8 820eedfd Wdf01000!FxDriver::Unload+0xa1 [minkernel\wdf\framework\shared\core\fxdriver.cpp @ 179]
8afabbc4 820eedfd c1f0c5a8 89e51540 8f436040 KMDFdrv!FxStubDriverUnload+0x1a [d:\th\minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 159]
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> ?? drvObj
void * 0x5997b8a8
kd> !wdfhandle 0x5997b8a8

Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 1 ;<----引用计数值==1
Contexts:
    context:  dt 0xa6684830 DrvCtx (size is 0xc bytes)
    EvtCleanupCallback ab0b10a0 KMDFdrv!DrvCleanupCallback
    EvtDestroyCallback ab0b10c0 KMDFdrv!DrvDestroyCallback

!wdfobject 0xa6684750

    执行完DisposeChildrenWorker后,旋即进入FxObject::DeletedAndDisposedWorkerLocked函数:

VOID
FxObject::DeletedAndDisposedWorkerLocked(
    __in __drv_when(Unlock, __drv_restoresIRQL) KIRQL OldIrql,
    __in                                        BOOLEAN Unlock
    )
{
    SetObjectStateLocked(FxObjectStateDeletedAndDisposed); //设置FxObject状态

    if (Unlock) {
        m_SpinLock.Release(OldIrql);
    }
    //对应这个空驱动,FxDriver没有子对象,所以就不跟进这个函数
    DestroyChildren();
    //敲黑板划重点
    RELEASE(NULL);
}

RELEASE(NULL)是段宏,因为source insight中满是Release的引用,从茫茫人海中定位那个它,真的很困难:

//FxObject.hpp
#define RELEASE(_tag)   Release(_tag, __LINE__, __FILE__)

virtual ULONG Release(PVOID Tag = NULL,LONG Line = 0,PCSTR File = NULL)
{
	ULONG c;
	c = InterlockedDecrement(&m_Refcnt);
	if (c == 0) {
		FinalRelease();
	}
	return c;
}

    执行RELEASE时,会减少引用计数,如果引用计数归零,则调用FinalRelease,由它调用EvtDestroyCallback函数。之后,FxDriver对象的内存被框架释放,不宜再被使用。

VOID FxObject::ProcessDestroy(VOID)
{
	//...
	for (pHeader = GetContextHeader(); 
             pHeader != NULL; 
             pHeader = pHeader->NextHeader) {

		if (pHeader->EvtCleanupCallback != NULL) {
			pHeader->EvtCleanupCallback(h);
			pHeader->EvtCleanupCallback = NULL;
		}

		if (pHeader->EvtDestroyCallback != NULL) {
			pHeader->EvtDestroyCallback(h);
			pHeader->EvtDestroyCallback = NULL;
		}
	}
	...
	//
	SelfDestruct();	//SelfDestruct是delete this的封装
}
kd> kb
ChildEBP RetAddr  Args to Child              
8afabb80 8ad51245 5997b8a8 b8b14bc8 a6684750 KMDFdrv!DrvDestroyCallback [g:\kmdfdrv\kmdfdrv\kmdf.c @ 20]
8afabba8 8ad7c310 c1763a40 8f436040 8afabbc4 Wdf01000!FxObject::DeleteObject+0x24e65 [minkernel\wdf\framework\shared\object\fxobjectstatemachine.cpp @ 124]
8afabbb8 ab0b1324 c1f0c5a8 8afabbe8 820eedfd Wdf01000!FxDriver::Unload+0xa1 [minkernel\wdf\framework\shared\core\fxdriver.cpp @ 179]
8afabbc4 820eedfd c1f0c5a8 89e51540 8f436040 KMDFdrv!FxStubDriverUnload+0x1a [d:\th\minkernel\wdf\framework\kmdf\src\dynamic\stub\stub.cpp @ 159]
8afabbe8 81ecac5f c1763a40 00000000 8f436040 nt!IopLoadUnloadDriver+0x87
kd> !wdfhandle 0x5997b8a8

Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 0 ;引用计数值==0

!wdfobject 0xa6684750
kd> !wdfobject 0xa6684750
State: FxObjectStateDeletedAndDisposed (0xa) ;FxObject状态

在EvtDestroyCallback中,引用计数已经为0,这种情况下再减少引用计数会造成数据溢出,所以很多书在介绍EvtCleanupCallback时会提到减少FxObject引用计数,却从没见过有哪本书说在EvtDestroyCallback中减少引用计数。虽然两者都可以做一些资源释放相关的操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值