概述:内核定时器使用说明
参考文章
内核定时器
内核定时器分为两种,即 IO 定时器和 DPC 定时器,一般来说,IO定时器是DDK(Driver Developer Kit)中提供的一种,该定时器可以为间隔为N秒做定时,但如果要实现毫秒级别间隔,微妙级别间隔,就需要用到DPC定时器,如果是秒级别定时基本上两者没有差别。
IO定时器
内核I/O定时器(Kernel I/O Timer)是Windows内核中的一种定时器机制,允许内核或驱动程序在指定的时间间隔内调用一个回调函数。以下是关于内核I/O定时器的详细介绍:
特点
- 精度:内核I/O定时器的精度通常为秒级。
- 依赖设备对象:内核I/O定时器必须依附于一个设备对象。
使用步骤
- 初始化定时器:调用
IoInitializeTimer函数对定时器进行初始化。此函数每个设备对象只能调用一次。
- 启动定时器:调用
IoStartTimer函数使定时器开始运行。
- 停止定时器:调用
IoStopTimer函数关闭定时器。
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <ntifs.h> #include <wdm.h> #include <ntstrsafe.h>
LONG count = 0;
VOID MyTimerProcess(__in struct _DEVICE_OBJECT *DeviceObject, __in_opt PVOID Context) { InterlockedIncrement(&count); DbgPrint("定时器计数 = %d", count); }
VOID UnDriver(PDRIVER_OBJECT driver) { IoStopTimer(driver->DeviceObject);
IoDeleteDevice(driver->DeviceObject);
DbgPrint("Uninstall Driver Is OK \n"); }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint("hello DriverEntry \n");
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING dev_name = RTL_CONSTANT_STRING(L""); PDEVICE_OBJECT dev; status = IoCreateDevice(Driver, 0, &dev_name, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dev); if (!NT_SUCCESS(status)) { return STATUS_UNSUCCESSFUL; } else { IoInitializeTimer(dev, MyTimerProcess, NULL); IoStartTimer(dev); }
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|
注意事项
- 内核I/O定时器的回调函数会在系统线程上下文中执行。
- 如果需要更高精度的定时器,可以考虑使用DPC定时器。
DPC 定时器
内核DPC(Deferred Procedure Call,延迟过程调用)定时器是一种基于DPC机制的定时器,用于在内核中实现高精度的定时任务。以下是关于内核DPC定时器的特点和使用步骤的详细介绍:
特点
- 高精度:DPC定时器可以实现毫秒甚至微秒级别的定时。
- 延迟执行:DPC定时器允许系统在未来的某个时间点执行任务,而不是立即执行,这有助于避免中断处理例程(ISR)执行时间过长。
- 中断上下文之外执行:DPC定时器的执行不在中断服务例程的上下文中,避免了在ISR中执行过长时间的任务,从而提高系统的响应性和稳定性。
- 任务调度:DPC定时器允许内核将需要延迟执行的任务排队,并在指定的时间点执行这些任务。
使用步骤
-
初始化定时器对象: 使用KeInitializeTimer函数初始化一个KTIMER对象。
c复制
1
| VOID KeInitializeTimer(PKTIMER Timer);
|
-
初始化DPC对象: 使用KeInitializeDpc函数初始化一个KDPC对象,并设置与DPC关联的定时器例程。
c复制
1
| VOID KeInitializeDpc(PRKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine, PVOID DeferredContext);
|
-
设置定时器: 使用KeSetTimer函数设置定时器的触发时间,并将DPC对象与定时器关联。
c复制
1
| BOOLEAN KeSetTimer(PKTIMER Timer, LARGE_INTEGER DueTime, PKDPC Dpc);
|
Timer是定时器对象的指针。
DueTime表示定时器触发的时间间隔。
Dpc是DPC对象的指针。
-
取消定时器: 如果需要取消定时器,可以调用KeCancelTimer函数。
c复制
1
| BOOLEAN KeCancelTimer(PKTIMER Timer);
|
-
周期性触发: 如果需要周期性触发DPC例程,需要在DPC例程中再次调用KeSetTimer函数。
示例代码
以下是一个简单的DPC定时器使用示例:
c复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <ntifs.h> #include <wdm.h>
LONG count = 0; KTIMER g_ktimer; KDPC g_kdpc;
VOID MyTimerProcess(__in struct _KDPC *Dpc, __in_opt PVOID DeferredContext, __in_opt PVOID SystemArgument1, __in_opt PVOID SystemArgument2) { LARGE_INTEGER la_dutime = { 0 }; la_dutime.QuadPart = 1000 * 1000 * -10;
InterlockedIncrement(&count);
DbgPrint("DPC 定时执行 = %d", count);
KeSetTimer(&g_ktimer, la_dutime, &g_kdpc); }
VOID UnDriver(PDRIVER_OBJECT driver) { KeCancelTimer(&g_ktimer);
DbgPrint("Uninstall Driver Is OK \n"); }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { LARGE_INTEGER la_dutime = { 0 }; la_dutime.QuadPart = 1000 * 1000 * -10;
KeInitializeTimer(&g_ktimer);
KeInitializeDpc(&g_kdpc, MyTimerProcess, NULL);
KeSetTimer(&g_ktimer, la_dutime, &g_kdpc);
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|
注意事项
- 在DPC例程中,需要再次调用
KeSetTimer以实现周期性触发。
- 如果需要停止DPC定时器,必须调用
KeCancelTimer,否则可能导致系统不稳定。
获取系统精确时间
在内核中通过 KeQuerySystemTime 获取是的系统时间是标准时间(GMT+0),转换成本地时间还需使用RtlTimeToTimeFields函数将其转换为TIME_FIELDS结构体格式。
相关代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <ntifs.h> #include <wdm.h> #include <ntstrsafe.h>
VOID MyGetCurrentTime() { LARGE_INTEGER CurrentTime; LARGE_INTEGER LocalTime; TIME_FIELDS TimeFiled; KeQuerySystemTime(&CurrentTime); ExSystemTimeToLocalTime(&CurrentTime, &LocalTime); RtlTimeToTimeFields(&LocalTime, &TimeFiled);
DbgPrint("[时间与日期] %4d年%2d月%2d日 %2d时%2d分%2d秒", TimeFiled.Year, TimeFiled.Month, TimeFiled.Day, TimeFiled.Hour, TimeFiled.Minute, TimeFiled.Second); }
VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK \n")); }
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { MyGetCurrentTime();
DbgPrint("hello lyshark \n");
Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }
|