KMDF和UMDF都使用引用计数,所有的对象都有引用计数:
UMDF使用AddRef和Release来管理引用计数;
KMDF使用WdfObjectReferenceXxx和WdfObjectDereferenceXxx维护引用计数
对象层次:
如图,驱动对象是根,其它都是它的后代。设备总是驱动的儿子,queue通常是设备的儿子,也可能是a later descendant
由framework构建及维护internal object tree
注意:驱动创建对象时,要正确设置父对象
默认父对象为驱动
若memory object只在I/O request,或者I/O target中用,需要设置父对象为他们
当对象被删除时,framework会首先调用最近一层子对象的cleanup callback,再调用自己的
driver通常不持有自己创建对象的reference,但是对要保证valid的对象时,需要持有reference,但是在删除那些对象时,需要释放reference(实现对象的cleanup callback)
名词解释:
destruction:driver调用framework来删除对象时,最终会导致对象的destruction。第一步就是dispose对象的儿子(调用它们的cleanup callback)。dispose后,reference仍被child持有。当对象的最后一个reference移除后,framework调用对象的deletion callback,最终destroy对象。
UMDF对象层次
注意虚线,可以根据需要设置parent
当reference的计数为0时,可以调用IWDFObject::DeleteWdfObject
驱动在使用完pointer后需要调用Release:
可以对object的IUnknown 调用AddRef和Release(通常是不需要的),但若driver使用了object的reference,必须实现IObjectCleanup::OnCleanup接口
OnCleanup解决了COM的circular reference问题,用external event来打破circular
避免circular reference:使用弱引用(不增加引用计数)
KMDF对象层次
图中表示了parent chain关系:例如WDFDEVICE可以成为WDFQUEUE的曾祖父
若KMDF调用了删除了一个object方法,KMDF仅仅对object做一个标记,直到object的reference count到0时才会释放object占用的内存
KMDF driver只有在要保证一个object可用时才要显式管理object的reference,而且必须在删除object前在cleanup callback中调用WdfObjectDereference。
Object Deletion
触发deletion:
1.object 的父亲被删除
2.driver显式调用删除object方法:UMDF,IWDFObject::DeleteWdfObject; KMDF,WdfObjectDelete
framework和driver都可以控制删除,下表为driver是否可以删除一种object(只有允许被driver删除并且在driver控制之下的object能被删除):
Object | ||
---|---|---|
Child list | Not applicable | No |
Collection | Not applicable | Yes |
Device | No | No, except for control device objects and PDOs in a certain state |
DMA common buffer | Not applicable | Yes |
DMA enabler | Not applicable | Yes |
DMA transaction | Not applicable | Yes |
DPC | Not applicable | Yes |
Driver | No | No |
Driver-created file | Yes | Yes |
File | No | No |
Base or Generic object | Yes, if created by the driver | Yes, if created by the driver |
I/O queue | No, for default I/O queue Yes, for other queues | Yes |
I/O request | No, if created by the framework Yes, if created by the driver | No, if created by the framework Yes, if created by the driver |
I/O target | No, for the default I/O target Yes, for all other targets | No, for the default I/O target Yes, for all other targets |
Interrupt | Not applicable | No |
Lookaside list | Not applicable | Yes |
Memory | No, if created by the framework Yes, if created by the driver | No, if created by the framework Yes, if created by the driver |
Named property store | No | Not applicable |
Registry key | Not applicable | Yes |
Resource list | Not applicable | No |
Resource range list | Not applicable | Yes |
Resource requirements list | Not applicable | No |
String | Not applicable | Yes |
Synchronization: spin lock | Not applicable | Yes |
Synchronization: wait lock | Not applicable | Yes |
Timer | Not applicable | Yes |
USB device | No, for the default target Yes, for all other targets | Yes |
USB interface | No, for the default target Yes, for all other targets | No |
USB pipe | No, for the default target Yes, for all other targets | No |
WMI instance | Not applicable | Yes |
WMI provider | Not applicable | No |
Work item | Not applicable | Yes |
framework控制着大多数object及生命周期,如device object和default I/O target object,framework会在一处device时自动删除它们
只有在driver具有object时,driver才能显式地删除object。
当driver调用delete方法时,framework的行为:
1.在callback object的interface上释放引用(UMDF only)
2.无论reference count多少,先调用object的cleanup callback函数
3.递减reference count
4.标记object为delete
5.当reference count为0时,删除object,并从object tree中移除
若driver未显式delete object,framework在它的parent被删除时删除该object
driver不能调用由framework控制的object的删除方法,否则:
UMDF中,driver stop with error
KMDF中,system bug check
Cleanup Callbacks
任何对象都支持
在删除object(或其parent)前,framework调用cleanup callback
cleanup的行为:释放任何object维护的outstanding references;释放object被分配的资源;做任何在deleted前需要的操作。
UMDF:callback object的IObjectCleanup interface的OnCleanup方法
KMDF:EvtCleanupCallback,在删除时且递减ref count前调用,该callback在创建object的attrivutes structure中注册。
Destroy Callbacks
driver可以实现一个destroy callback替换或附加到cleanup callback中
UMDF:
UMDF没有destroy callback,在ref count到0时自动析构对象(行为和destroy一样)
driver可以释放它对object分配的资源,但是无法决定在framework中删除它。
KMDF:
EvtDestroyCallback,注册在attrivutes structure中
调用顺序:对object及其所有parent 的cleanup callback完成(return),ref count变为0,然后是destroy callback,最后delete object
UMDF Object Deletion
driver创建的I/O request object通常是UMDF driver需要显式delete的
例子表示Fx2_Driver创建、发送和删除request(completion callback),
在IWDFRequestCallbackRequestCompletion::OnCompletion中调用IWDFObject::DeleteWdfObject的例子(Device.cpp)
VOID CMyDevice::OnCompletion( IWDFIoRequest* FxRequest, IWDFIoTarget* pIoTarget, IWDFRequestCompletionParams* pParams, PVOID pContext ) { . . . //Code omitted HRESULT hrCompletion = pParams->GetCompletionStatus(); if (FAILED(hrCompletion)) { m_InterruptReadProblem = hrCompletion; } else { // Get the returned parameters and other information . . . //Code omitted } FxRequest->DeleteWdfObject(); . . . //Code omitted }
driver要在DeleteWdfObject操作之前获取到所有信息,因为delete后,driver无法访问object了
KMDF Object Deletion
删除YourObject步骤:
1.调用object tree中YourObject最远端后代的EvtCleanupCallback (用来释放显式的ref)
2.重复1.直到YourObject的EvtCleanupCallback
3.按相同顺序遍历tree,移除framework对object的reference。若counter到0,调用EvtDestroyCallback方法,最后释放分配到object和context area的内存
*不保证同一级的子孙调用cleanup的顺序
*framework精细地完成了同时在不同线程上的object的删除方法,通过一个driver-global dispose list完成
----KMDF中调用EvtCleanupCallback顺序---
依据层次删除object时能保证parent的存在,但driver显式调用object的EvtCleanupCallback时,无法保证父亲仍然存在。
一些对象的EvtCleanupCallback可以在PASSIVE_LEVEL或DISPATCH_LEVEL级别调用,但是下列对象只能在PASSIVE_LEVEL级别中调用:
WDFDEVICE | WDFQUEUE |
WDFDPC | WDFSTRING |
WDFIOTARGET | WDFTIMER |
WDFLOOKASIDE objects that allocate paged pool | WDFWORKITEM |
WDFMEMORY objects that contain paged pool |
当objcet要等待某事完成或者访问了paged内存时,需要cleanup运行在PASSIVE_LEVEL中。
object的cleanup在DISPATCH_LEVEL中时,KMDF会把PASSIVE_LEVEL的任务推迟并放入一个work item,KMDF为每个device object维护一个work item,按顺序调用cleanup
需要在PASSIVE_LEVEL中cleanup的object不一定必须在PASSIVE_LEVEL中create。
WDFWORKITEM, WDFTIMER, WDFDPC, 和 WDFQUEUE,以及使用了nonpaged pool的WDFMEMORY和WDFLOOKASIDE能在DISPATCH_LEVEL中创建。
使用paged pool的WDFMEMORY和WDFLOOKASIDE,以及WDFDEVICE, WDFIOTARGET, WDFCOMMONBUFFER, WDFKEY, WDFCHILDLIST, WDFSTRING不能在DISPATCH_LEVEL中创建。
例外:Completed I/O request
在I/O request object的子对象中不能有任何需要在PASSIVE_LEVEL中运行cleanup的对象。
由于IRP要尽快完成,若I/O request object有子对象timer object,KMDF会先完成IRP,删除I/O request object,在调用PASSIVE_LEVEL中的timer object的cleanup,导致出现父对象先被释放的错误!
-----KMDF调用EvtDestroyCallback的顺序----
在cleanup和ref counter到0后才会调用Destroy
destroy可以不遵循先子后父顺序
destroy callback能访问object context area,但不能访问object的任何方法
KMDF不能保证调用destroy的IRQL级别
Echo的Driver.c的object deletion例子:
NTSTATUS status; WDFSTRING string; WDF_DRIVER_VERSION_AVAILABLE_PARAMS ver; status = WdfStringCreate(NULL, WDF_NO_OBJECT_ATTRIBUTES, &string); if (!NT_SUCCESS(status)) { . . .//Code omitted } status = WdfDriverRetrieveVersionString(WdfGetDriver(), string); if (!NT_SUCCESS(status)) { . . .//Code omitted } . . .//Code omitted WdfObjectDelete(string); string = NULL; // To avoid referencing a deleted object.图解:对string调用WdfObjectDelete