在上一篇文章http://www.youranshare.com/codeorg/sid/130.html中介绍了驱动程序中I/O定时器的使用方法,但是对于I/O定时器来说其精度只有1S,使用较为不方便,本文中将会介绍驱动程序中定时器的另一种实现方式,DPC定时器,DPC定时器精度为100ns,相比I/O定时器来说使用更为灵活.
DCP是一种使用更加灵活的定时器,可以对任意间隔时间进行定时。DPC定时器的内部使用了一个定时器对象KTIMER,当你设定了定时器之后,从设定开始起经过这个时间之后操作系统会将一个DPC定时器的例程插入到DPC的队列,操作系统读取DPC队列的时候定时器例程就能够被执行。这里的DPC定时器例程就相当于一个定时器的回调函数。
和I/O定时器类似,在使用之前你需要一些相关的初始化操作,你需要初始化DPC对象和定时器对象。
初始化DPC使用内核函数KeInitializeTimer,他的声明如下:
VOID KeInitializeTimer ( __out PKTIMER Timer );
其中参数Timer是定时器的指针,它是一个输出值.
初始化DPC对象使用内核函数KeInitializeDpc,它的声明如下:
VOID KeInitializeDpc ( __out __drv_aliasesMem PRKDPC Dpc, __in PKDEFERRED_ROUTINE DeferredRoutine, __in_opt __drv_aliasesMem PVOID DeferredContext );
参数1:Dpc是输入参数DPC的对象指针
参数2:DeferredRoutine是一个与DPC关联的定时器例程函数,当定时器被触发的时候这个例程函数就会被执行调用
参数3:DeferredContext是一个传入定时器例程的参数.
当初始化完成之后使用函数KeSetTimer函数开启定时器,它的声明如下:
BOOLEAN KeSetTimer ( __inout PKTIMER Timer, __in LARGE_INTEGER DueTime, __in_opt PKDPC Dpc );
参数1:Timer是定时器对象的指针.
参数2:DueTimer这个参数是设定的时间间隔.
参数3:Dpc表示传入定时器例程的参数.
返回值:BOOL类型的,表示成功或者失败.
取消定时器使用内核函数KeCancelTimer,它的声明如下:
BOOLEAN KeCancelTimer ( __inout PKTIMER );
参数:在调用KeSetTimer函数之后,经过DueTime的时间,操作系统会调用一次定时器的例程,其中如果DueTime是一个正整数,则会代表绝对时间,也就是相对于1601年1月1日到触发DPC定时器的那个时刻,如果DueTime是负数,那它表示的是间隔多长时间,这里需要说的是DueTime的单位可不是1ns,它的单位是100ns。
在调用了KeSetTimer之后,只会触发一次DPC定时器例程,如果想要周期的触发DPC定时器例程,则需要在DPC定时器例程被触发后再次调用KeSetTimer函数。
① 首先定义两个IOCTL,用于控制定时器的开启和取消
② 在你的设备扩展中添加上KDPC对象和KTIMER成员,以备后用
③ 直接使用设备扩展中的KDPC和KTIMER对象完成初始化,
并将设备扩展的指针作为定时器例程的参数.
④ 在IRP_MJ_DEVICE_CONTROL例程中完成对定时器的开启和关闭.
附加一段我们的设备扩展的结构体:
typedef struct _DEVICE_EXT{ //指向设备的 PDEVICE_OBJECT pDevice; UNICODE_STRING USzDeviceName;//设备名称 UNICODE_STRING USzSymLinkName;//符号链接名称 KDPC pollingDPC;//DPC对象 KTIMER pollingTimer;//定时器对象 LARGE_INTEGER pollingInterval;//定时器间隔 }DEVICE_EXTENSION,*PDEVICE_EXTENSION;
需要说明的几点:DPC的定时器和I/O定时器一样也是可以运行于任意线程中的,并且定时器例程的IRQL中断请求级是DISPATH_LEVEL,需要使用非分页内存的.
下面是示例代码工程的下载地址:
解压密码 yscode
百度网盘地址http://pan.baidu.com/s/1ntiauSx