万万没想到<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 0x5997b8a8Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 1 ;<----引用计数值==1
Contexts:context:  dt 0xa6684830 DrvCtx (size is 0xc bytes)EvtCleanupCallback ab0b10a0 KMDFdrv!DrvCleanupCallbackEvtDestroyCallback 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 0x5997b8a8Dumping WDFHANDLE 0x5997b8a8
=============================
Handle type is WDFDRIVER
Refcount: 0 ;引用计数值==0!wdfobject 0xa6684750
kd> !wdfobject 0xa6684750
State: FxObjectStateDeletedAndDisposed (0xa) ;FxObject状态

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

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

  1. Wdf框架之WdfObject状态机(2)

    前一篇博文<Wdf框架之WdfObject状态机(1)>提到调用WdfObjectCreate使框架对象被纳入对象状态机的管理之下.本篇我们看下框架对象的销毁过程,即如何结束它的生命周期, ...

  2. Wdf框架之WdfObject状态机(3)-前篇

    好久没写关于Wdf框架的博客了,因为有各种琐碎事缠身,得赶在RS4 RTM前把状态机(3)系列完成.WdfObject状态机(2)系列将注意力集中在驱动程序的继承层次上只存在单薄一层WdfDriver ...

  3. QP状态机框架与常见状态机方法

    原文链接:嵌入式状态机编程-QP状态机框架与常见状态机方法 状态机基本术语 现态:是指当前所处的状态. 条件:又称为"事件",当一个条件被满足,将会触发一个动作,或者执行一次状态的 ...

  4. 一线互联网架构师设计思想解读开源框架!附超全教程文档

    前言 最近一段时间发现经常看到很多人,对Spring源码比较感兴趣,日常开发中,无论你做什么什么项目,大部分都离不开Spring生态的那一套东西,所以很多人对Spring底层源码实现很感兴趣,但是有些 ...

  5. 智源学者文再文获北京市杰出青年中关村奖

    8月26日,北京市人民政府公布"关于2019年度北京市科学技术奖励的决定".根据<北京市科学技术奖励办法>规定,经市科学技术奖励评审委员会评审.市科学技术奖励委员会审定 ...

  6. CC框架实践(1):实现登录成功再进入目标界面功能

    在掘金上看到这篇文章:android 关于先登录成功后再进入目标界面的思考,作者对实现登录成功后再跳转到目标界面功能作了比较详细的分析,对比了一些已有的实现方案并指出存在的问题.最终,作者实现了一个可 ...

  7. 对排除VLAN中Trunk配置故障一文的补充

    对排除VLAN中Trunk配置故障一文的补充                simeon    在<网管员世界>第4B中的<排除VLAN中Trunk配置故障>中对VLAN的Tr ...

  8. 【前端】【element】el-progress组件使用文档补充——大小调整与数字颜色

    [前端][element]el-progress组件使用文档补充--大小调整与数字颜色 el-progress 调整大小 调整颜色 基础可参考官方文档:Progress 进度条 el-progress ...

  9. 三十、Pyspider爬虫框架总结,爬取Scrapy文档

    这是我Python培训的内容,使用Pyspider框架爬取Scrapy文档 @Author:xinlan pyspider框架 一.pyspider框架介绍 1.简介 pyspider 是个强大的由p ...

最新文章

  1. 智能驾驶开发的几个问题
  2. IEEE分享 | 机器学习在领英的规模化应用
  3. 【原创】如何在 Linux 下调整可打开文件/文件描述符数目
  4. easyui分页查询为什么会有下拉框_做网站优化为什么要分析百度下拉词和相关搜索?...
  5. c语言手机通讯录退出程序,通讯录小程序(C/C++)C语言练习小程序
  6. 图解HTTP学习记录(六)
  7. 多功能监护系统开发与设计
  8. Excel VBA 处理图形图表详解
  9. LeetCode 678 有效的括号字符串,常规栈思路
  10. python语言的基础知识_pythone语言基础知识汇总
  11. pdf是文件还是文档
  12. Flutter获取assets中的图像
  13. Android 原创新作 超级水平仪 发布
  14. 企业数字化转型之道(值得收藏)
  15. 西刺代理python_python爬取西刺代理所有数据 !
  16. 需求分析-业务需求、用户需求、功能需求
  17. 51单片机自学笔记引脚
  18. FragmentActivity_左右滑动的碎片
  19. 如何关闭win10自带杀毒?
  20. react hook 闭包陷阱问题

热门文章

  1. 网页前端简单制作(超级课程表)
  2. DeprecationWarning: There is no current event loop loop = asyncio.get_event_loop()
  3. 网站文件上传到服务器怎么不显示,我已经上传文件,为什么还看不到网站首页?...
  4. 第三章:MATLAB的基础知识(基本符号,数据类型,运算符,复数运算,三角函数运算)
  5. java开发crm框架_这可能是2020年度最完整、详细的Java高级框架+CRM课程哟,小白看完直呼过瘾!...
  6. 打造个人知识库网站(Docsify+GitHub Page)
  7. 2022年四月新闻舆情事件分析集锦
  8. Android 动画详尽教程 [详尽!详尽!]
  9. 十六、Vert.x、Actix-web、Warp、Axum 性能对比
  10. Bottle web framework