Windows驱动
+ -

Windows内核驱动定时器

2021-07-01 319 0

使用过Windows应用程序编程的读者的读者一定对SetTimer()映像尤深。当需要定时执行任务的时候,SetTimer()变得非常重要。这个功能在驱动开发中可以通过一些不同的替代方法来实现。比较经典的对应是KeSetTimer(),这个函数的原型如下:

BOOLEAN    KeSetTimer(
    IN PKTIMER  Timer,                // 定时器
    IN LARGE_INTEGER  DueTime,    // 延后执行的时间
    IN PKDPC  Dpc  OPTIONAL        // 要执行的回调函数结构
);

其中的定时器Timer和要执行的回调函数结构Dpc都必须先初始化。其中Timer的初始化比较简单。下面的代码可以初始化一个Timer:

    KTIMER my_timer;
    KeInitializeTimer(&my_timer);

Dpc的初始化比较麻烦。这是因为需要提供一个回调函数。初始化Dpc的函数原型如下:

VOID  KeInitializeDpc(
        IN PRKDPC  Dpc,
         IN PKDEFERRED_ROUTINE  DeferredRoutine,
         IN PVOID  DeferredContext
);

PKDEFERRED_ROUTINE这个函数指针类型所对应的函数的类型实际上是这样的:

VOID CustomDpc(
        IN struct _KDPC  *Dpc,
        IN PVOID  DeferredContext,
        IN PVOID  SystemArgument1,
        IN PVOID  SystemArgument2
 );

读者需要关心的只是DeferredContext。这个参数是KeInitializeDpc调用时传入的参数。用来提供给CustomDpc被调用的时候,让用户传入一些参数。
至于后面的SystemArgument1和SystemArgument2则请不要理会。Dpc是回调这个函数的KDPC结构。

请注意这是一个“延时执行”的过程。而不是一个定时执行的过程。因此每次执行了之后,下次就不会再被调用了。如果想要定时反复执行,就必须在每次CustomDpc函数被调用的时候,再次调用KeSetTimer,来保证下次还可以执行。

值得注意的是,CustomDpc将运行在APC中断级。因此并不是所有的事情都可以做(在调用任何内核系统函数的时候,请注意WDK说明文档中标明的中断级要求。)
这些事情非常的烦恼,因此要完全实现定时器的功能,需要自己封装一些东西。下面的结构封装了全部需要的信息:

 // 内部时钟结构
 typedef struct MY_TIMER_
 {
     KDPC dpc;
     KTIMER timer;
     PKDEFERRED_ROUTINE func;
     PVOID private_context;
 } MY_TIMER,*PMY_TIMER;

 // 初始化这个结构:
 void MyTimerInit(PMY_TIMER timer, PKDEFERRED_ROUTINE func)
 {
     // 请注意,我把回调函数的上下文参数设置为timer,为什么要
     // 这样做呢?
     KeInitializeDpc(&timer->dpc,sf_my_dpc_routine,timer);
     timer->func = func;
     KeInitializeTimer(&timer->timer);
     return (wd_timer_h)timer;
 }

 // 让这个结构中的回调函数在n毫秒之后开始运行:
 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context)
 {
     LARGE_INTEGER due;
     // 注意时间单位的转换。这里msec是毫秒。
     due.QuadPart = -10000*msec;
     // 用户私有上下文。
     timer->private_context = context;
     return KeSetTimer(&timer->timer,due,&mytimer->dpc);
 };

 // 停止执行
 VOID MyTimerDestroy(PMY_TIMER timer)
 {
     KeCancelTimer(&mytimer->timer);
 };

使用结构PMY_TIMER已经比结合使用KDPC和KTIMER简便许多。但是还是有一些要注意的地方。真正的OnTimer回调函数中,要获得上下文,必须要从timer->private_context中获得。此外,OnTimer中还有必要再次调用MyTimerSet(),来保证下次依然得到执行。

VOID
MyOnTimer (
        IN struct _KDPC  *Dpc,
        IN PVOID  DeferredContext,
        IN PVOID  SystemArgument1,
        IN PVOID  SystemArgument2
    )
{
    // 这里传入的上下文是timer结构,用来下次再启动延时调用
    PMY_TIMER timer = (PMY_TIMER)DeferredContext;
    // 获得用户上下文
    PVOID my_context = timer->private_context;
    // 在这里做OnTimer中要做的事情
    ……
    // 再次调用。这里假设每1秒执行一次
    MyTimerSet(timer,1000,my_context);
};

关于定时器就介绍到这里了。

0 篇笔记 写笔记

Windows内核驱动定时器
使用过Windows应用程序编程的读者的读者一定对SetTimer()映像尤深。当需要定时执行任务的时候,SetTimer()变得非常重要。这个功能在驱动开发中可以通过一些不同的替代方法来实现。比较经典的对应是KeSetTimer(),这个函数的原型如下:BOOLEAN KeSetTimer......
timeSetEvent定时器
时间比较准的定时器timeSetEvent。头文件:#include #pragma comment(lib,"Winmm.lib")如定时精度为1ms,定时为20ms的定时器。#define TIME_STEP 20 ti......
Windbg和IDA配合解决已卸载的驱动DPC定时器引起的蓝屏DRIVER_IRQL_NOT_LESS_OR_EQUAL
最近搞了一个虚拟的设备驱动,自己测试都没有问题,不过拿给同事正式用的时候,会出现蓝屏问题。按他来说,好像成了必现问题。今天一大早,斜风细雨,天气凉爽,正是揪出这个BUG的好时机,说干就干。自己先是在调试机中模拟同事的试验方法,可惜的是,试了多次均没有复现。还真是奇怪了,没办法,自己只能不起寻常路了。......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!