Windows Driver Foundation - KMDF 内核模式驱动框架结构 第二部分

KMDF对象模型

KMDF定义了以对象为基础的编程模型,其中对象类型代表了常见的驱动结构。每个对象输出方法(功能)和属性(数据)给驱动访问,并且关联到对象指定的事件,驱动可以通过提供事件回调函数来支持事件。对象本身对驱动来说是不透明的。

KMDF代替驱动创建了一些对象,驱动根据自己特定的需求创建其他对象。驱动也可以提供事件回调函数来处理KMDF默认动作不能满足设备的情况,并且可以调用对象的方法来读写属性和执行额外的动作。所以,KMDF驱动实质上就是,一个DriverEntry例程,一套设备指定任务的回调函数,以及驱动实现功能需要的所有工具函数。

框架下的驱动从不直接访问框架对象的实例。相反的,他们通过句柄引用对象实例,驱动把句柄作为参数传递给对象方法,而KMDF把它们传递给事件回调函数。框架对象对框架而言是唯一的。它们不由Windows对象管理器管理,也不能通过系统ObXxx函数操作。只有框架(和它的驱动)能创建并操作它们。

方法,属性和事件

方法是在对象上执行动作的函数,比如创建和删除对象。KMDF方法如下方案命名:

WdfObjectOperation

 

Object指定方法操作的KMDF对象,Operation指明方法做什么。比如,WdfDeviceCreate方法创建一个框架设备对象。

属性是读写对象数据域的函数,也就定义了对象行为和默认操作。属性如下方案命名:

WdfObject{Set|Get}Data

WdfObject{Assign|Retrieve}Data

 

Object指定函数操作的KMDF对象,Data指定函数读写的域。一些属性读写不会失败,而一些有时会失败。名字中有SetGet的函数不会失败。Set函数返回空,Get函数一般会返回域的值。名字中有AssignRetrieve的函数读写域时会失败。这些函数返回NTSTATUS值。

比如,WDFINTERRUPT对象代表了设备的中断对象。每个中断对象由一套特性描述,它们指明了中断类型(消息信号还是基于IRQ)并提供了额外的中断信息。WdfInterruptGetInfo方法返回这些信息。相应的设置方法不可行,因为驱动在创建中断对象时初始化这些信息并且在设备操作时不能更改。

事件代表了驱动能相应的运行状态,或者在何种状态中驱动能够参与。驱动只为对自身操作有用的事件注册回调函数。当事件发生时,框架引用这些回调,并把注册该函数的对象句柄作为参数传入。比如,设备弹出是一个PNP事件。如果设备能被弹出,它的驱动就注册一个EvtDeviceEject回调例程,在弹出时完成设备操作。当PNP管理器发送IRP_MN_EJECT 请求给设备时,KMDF调用该例程,并把设备对象句柄传入。如果设备不能被弹出,驱动不需要这样的回调函数。

对于大多数事件,驱动要么提供一个回调例程,要么允许KMDF来响应默认动作。然而对于一些事件,必须要驱动指定回调函数。比如,添加设备就是一个PNP驱动必须为其注册回调函数的事件。驱动的EvtDriverDeviceAdd回调函数创建设备对象并设置设备属性。

KMDF事件和Windows用于同步机制的内核派遣事件没有关系。驱动不能创建,操作,或者等待一个KMDF事件。相反,驱动为其注册回调函数,而事件发生时KMDF调用驱动。(对于时间相关的等待,KMDF提供定时器对象)

 

对象层次结构

KMDF对象有层次地组织。WDFDRIVER是根对象;其他对象都可以认为是它的子对象。对于大多数对象类型,驱动能在创建时指定它们的父对象。如果创建时没有指定父对象,框架设置其默认父对象为WDFDRIVER对象。

图表1说明了默认的KMDF对象层次结构。

图表1.KMDF对象间的父子关系

对于每个对象,图表显示哪些对象必须在它的父链中。这些对象不一定是直接的父对象,也可以是它的祖父或曾祖父。比如,图表说明WDFDEVICE对象是WDFQUEUE对象的父对象。然而,WDFQUEUE也可以是WDFIOTARGET的子对象,后者又正好是WDFDEVICE的子对象。所以WDFDEVICE位于WDFQUEUE的父链中。

对象层次结构影响对象的生命周期。父对象给每个子对象的引用计数。当父对象删除时,子对象也被删除,它们的回调函数按定好的顺序调用。细节参见后面的“对象创建和删除”。

表格4列出了所有KMDF对象类型。

表格4.KMDF对象类型

对象

类型

描述

子列表

WDFCHILDLIST

列出设备的子设备。

汇集

WDFCOLLECTION

描述一列相似的对象,比如资源或者过滤驱动过滤其请求的设备。

设备

WDFDEVICE

表示设备实例。一般驱动控制的每个设备有一个WDFDEVICE对象。

DMA 通用缓存

WDFCOMMONBUFFER

表示一个实现DMA的缓存,可以被设备和驱动访问。

DMA 使能

WDFDMAENABLER

允许驱动使用DMA。处理设备I/O操作的驱动为设备的每个DMA通道提供一个WDFDMAENABLER对象。

DMA 传输

WDFDMATRANSACTION

表示单次DMA传输。

延迟过程调用(DPC)

WDFDPC

表示一个延迟过程调用。

驱动

WDFDRIVER

表示驱动本身,维护驱动信息,比如驱动入口。每个驱动都有一个WDFDRIVER对象。

文件

WDFFILEOBJECT

表示一个文件对象,外部驱动或应用可以通过它访问设备。

通用对象

WDFOBJECT

表示一个通用对象,根据驱动需求使用。

I/O 队列

WDFQUEUE

表示一个I/O队列。驱动可以有任意数量的WDFIOQUEUE对象。

I/O 请求

WDFREQUEST

表示一个设备I/O请求。

I/O 目标

WDFIOTARGET

表示一个设备栈,驱动向其转发I/O请求。

中断

WDFINTERRUPT

表示一个设备中断对象。处理设备中断的驱动为每个IRQ或者设备触发的消息信号中断(MSI)提供一个WDFINTERRUPT对象。

后备列表

WDFLOOKASIDE

表示一个动态大小的列表,包含从分页或非分页池分配的相同缓存。WDFLOOKASIDE对象和其内存缓存都能有属性,见后面的“对象属性”。

内存

WDFMEMORY

表示驱动使用的内存,一般是跟I/O请求相关的输入或输出缓存。

注册表键值

WDFKEY

表示一个注册表键值。

资源列表

WDFCMRESLIST

表示已经分配给设备的资源列表。

资源范围列表

WDFIORESLIST

表示一个设备的可能配置。

资源需求列表

WDFIORESREQLIST

表示一套I/O资源列表,包括了设备所有可能的配置。列表的每个元素是一个WDFIORESLIST对象。

字符串

WDFSTRING

表示一个计数的Unicode字符串。

同步:自旋锁

WDFSPINLOCK

表示一个自旋锁,在DISPATCH_LEVEL上同步访问数据。

同步:等待锁

WDFWAITLOCK

表示一个等待锁,在PASSIVE_LEVEL上同步访问数据。

定时器

WDFTIMER

表示一个定时器,单次地或周期地超时并调用回调例程。

USB 设备

WDFUSBDEVICE

表示一个USB设备。

USB 接口

WDFUSBINTERFACE

表示USB设备的一个接口。

USB 管道

WDFUSBPIPE

表示USB接口的一个管道,

Windows管理规范(WMI)实例

WDFWMIINSTANCE

表示一个关联到特定提供者的独立WMI数据块。

WMI 提供者

WDFWMIPROVIDER

表示驱动提供的WMI数据块方案。

工作项目

WDFWORKITEM

表示一个工作项目,运行在系统线程的PASSIVE_LEVEL上。

 

对象属性

每个KMDF对象有相关的一套属性。属性定义了KMDF需要的对象信息,如表格5所示。

表格5.KMDF对象属性

属性域

描述

ContextSizeOverride

上下文域的大小;覆盖ContextTypeInfo‑>ContextSize的值。对大小可变的上下文域有用。

ContextTypeInfo

指向对象上下文域的类型信息。

EvtCleanupCallback

指向回调函数例程,在对象删除前调用来清理对象;对象可能仍被引用。

EvtDestroyCallback

指向回调函数例程,当标记为删除的对象的引用计数到达零时调用。

ExecutionLevel

KMDF能调用某些对象回调函数时的最大中断请求等级(IRQL)。

ParentObject

对象的父对象句柄。

Size

对象大小。

SynchronizationScope

对象的某些回调函数同步时的等级;只适用于驱动,设备和文件对象。

 

框架给大多数属性提供默认值。驱动可以在创建对象时用WDF_OBJECT_ATTRIBUTES_INIT函数覆盖这些默认值。

 

对象上下文

每个KMDF对象的实例可以有一个或多个对象上下文域。该域就是驱动定义的跟特定对象实例相关的数据存储区域,如驱动分配的锁或对象事件。对象上下文的大小和布局由驱动决定。驱动创建对象时,初始化上下文域并指定大小和类型。驱动也可以在对象创建后建立额外的上下文域。对于KMDF设备对象,对象上下文域等价于WDM的设备扩展。

KMDF创建对象时,给上下文域分配非分页内存并根据驱动要求初始化。KMDF删除对象时,也删除上下文域。框架提供宏将类型和名字关联到上下文域,还提供宏来创建一个命名访问函数,用来返回上下文域的指针。

如果你熟悉WDM,这种设计可能看起来多此一举。然而,它提供了灵活性,使得在驱动中传递的I/O请求可以附加信息。此外,它让不同的库分别持有自己的对象上下文成为了可能。比如,IEEE 1394库记录一个WDFDEVICE对象的同时,设备功能驱动也可以用不同的上下文记录它。在驱动中,上下文域允许了一种类似于继承的设计方案。如果驱动为若干不同任务使用一个请求,请求对象可以为每个任务有不同的上下文域。特定任务的函数可以访问自己的上下文而不需要知道其他上下文是否存在或者包含什么内容。

对象创建和删除

为了创建一个对象,KMDF:

·         为对象和它的上下文域分配非分页的内存。

·         初始化设备属性,根据默认值和驱动指定(如果有)。

·         清零对象上下文域。

·         配置对象,存储事件回调函数指针,设置对象特定属性。

 

如果对象初始化失败,KMDF删除对象和已经创建的所有子对象。

为了初始化对象属性和配置结构,驱动在调用对象创建方法前先调用KMDF初始化函数。KMDF在创建对象时会使用已经初始化的属性和结构。

KMDF为每个对象维护一个引用计数,并确保在所有引用被释放前对象一直存在。如果驱动直接删除对象(通过调用删除方法),KMDF将对象标记为删除但是会等到它的引用计数值为零时才真正删除它。

驱动一般不获取它们创建对象的引用值,但是某些情况下(比如直接涉及到WDM)必须获得引用值来确保对象句柄仍然有效。比如,驱动发送了异步I/O请求,可能在取消时需要获取引用值来避免竞争情况。在请求对象删除前,驱动必须释放引用。

对象的删除从离父对象最远的对象开始,沿着对象层次结构往上直到根对象。KMDF按下列步骤来删除对象:

1.     从离父对象最远的子对象开始,调用其EvtCleanupCallback函数。在该例程中,驱动应该完成所有必须在父对象删除前进行的清理任务。这些任务可能包括释放对象或其父对象的直接引用。注意在EvtCleanupCallback函数运行时,该对象的子对象仍然存在,即使他们的EvtCleanupCallback函数已经调用过了。

2.   当对象的引用计数值到零时,如果驱动注册了,那么调用对象的EvtDestroyCallback函数。

3.   释放分配给对象和上下文域的内存。

 

KMDF总是在调用父对象的EvtCleanupCallback例程前先调用子对象的,所以驱动可以保证在子对象尔等EvtCleanupCallback运行时,其父对象是存在的。但是对于EvtDestroyCallbacks不能提供这种保证,KMDF可能以任何顺序调用EvtDestroyCallbacks例程,所以父对象的EvtDestroyCallbacks可能在子对象前调用。

通过设置ParentObject属性,驱动可以改变大多数KMDF对象的父对象。适当地设置父/子关系,驱动可以避免直接获得相关对象的引用,并用层次结构和相关回调函数来管理对象的生命周期。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值