定时器的实现
使用IO定时器
IO定时器每隔1s就会触发一次,从而进入到定时器例程中,如果某个操作是每n秒执行一次(n为正整数)可以考虑在定时器例程中记录一个计数器大小就为n,每次进入定时器例程中时将计数器减一,当计数器为0时,表示到达n秒,这个时候可以执行操作。IO定时器只适合处理整数秒的情况
在使用IO定时器之前需要对定时器进行初始化,初始化函数为IoInitializeTimer,定义如下:
NTSTATUS
IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject, //设备对象指针
IN PIO_TIMER_ROUTINE TimerRoutine,//定时器例程
IN PVOID Context//传给定时器例程的函数
);
初始化完成后可以使用IoStartTimer来启动定时器,使用IoStopTimer来停止定时器,下面是一个例子
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
typedef struct _tag_DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING uDeviceName;
UNICODE_STRING uSymbolickName;
LONG lTimerCount; //定时器触发时间,以秒为单位
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS DriverEntry(DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
LONG i;
PDEVICE_OBJECT pDeviceObject;
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
//设置派遣函数,这些代码在这就省略了
status = CreateDevice(DriverEntry, &pDeviceObject);
IoStartTimer(pDeviceObject);
return status;
}
#pragma LOCKEDCODE
VOID IoTimer(DEVICE_OBJECT *DeviceObject,PVOID Context)
{
LONG ret;
PDEVICE_EXTENSION pDeviceExtension;
UNICODE_STRING uProcessName;
PEPROCESS pCurrProcess;
UNREFERENCED_PARAMETER(Context);
pDeviceExtension = (PDEVICE_EXTENSION)(DeviceObject->DeviceExtension);
ASSERT(NULL != pDeviceExtension);
//采用互锁操作将定时器数减一
InterlockedDecrement(&pDeviceExtension->lTimerCount);
//判断当前时间是否到达3秒
ret = InterlockedCompareExchange(&pDeviceExtension->lTimerCount, TIME_OUT, 0);
if(0 == ret)
{
DbgPrint("3s time out\n");
}
pCurrProcess = IoGetCurrentProcess();
RtlInitUnicodeString(&uProcessName, (PTSTR)((ULONG)pCurrProcess + 0x174));
DbgPrint("the current process %wZ", uProcessName);
}
#pragma INITCODE
NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject,PDEVICE_OBJECT *ppDeviceObject)
{
NTSTATUS status;
UNICODE_STRING uDeviceName;
UNICODE_STRING uSymbolickName;
PDEVICE_EXTENSION pDeviceExtension;
RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);
RtlInitUnicodeString(&uSymbolickName, SYMBOLICK_NAME);
if(NULL != ppDeviceObject)
{
//创建设备对象并填充设备扩展中的变量
...
IoInitializeTimer(*ppDeviceObject, IoTimer, NULL);
status = IoCreateSymbolicLink(&uSymbolickName, &uDeviceName);
if(!NT_SUCCESS(status))
{
//出错的话就做一些清理工作
...
return status;
}
if(NULL != pDeviceExtension)
{
RtlInitUnicodeString(&pDeviceExtension->uSymbolickName, SYMBOLICK_NAME);
}
return status;
}
return STATUS_UNSUCCESSFUL;
}
需要注意的是IO定时器例程是位于DISPATCH_LEVEL,所以它不能使用分页内存,所以在函数前加上一句#pragma LOCKEDCODE,表示它在非分页内存中
DPC定时器
DPC定时器相比IO定时器来说更加灵活,它可以指定任何时间间隔。DPC内部使用KTIMER这个内核对象进行定时,每当时间到达设置的时间,那么系统就会将对应的DPC例程加入到DPC队列中,当系统读取DPC队列时,这个DPC例程就会被执行,使用DPC定时器的步骤一般是:
1. 分别调用KeInitializeTimer和KeInitializeDpc初始化KTIMER对象和DPC对象
2. 用KeSetTimer开启定时器
3. 在DPC例程中再次调用KeSetTimer开启定时器
4. 调用KeCancelTimer关闭定时器
由于每次执行KeSetTimer都只会触发一次DPC例程,所以如果想要周期性的调用DPC例程,需要在DPC例程中再次调用KeSetTimer。
这些函数的定义如下:
VOID
KeInitializeDpc(
IN PRKDPC Dpc, //DPC对象
IN PKDEFERRED_ROUTINE DeferredRoutine, //DPC例程
IN PVOID DeferredContext//传给DPC例程的参数
);
BOOLEAN
KeSetTimer(
IN PKTIMER Timer,//定时器
IN LARGE_INTEGER DueTime, //隔多久触发这个DPC例程,这个值是正数则表示从1601年1月1日到触发这个DPC例程所经历的时间,为负数,则表示从当前时间,间隔多长时间后触发,单位为100ns
IN PKDPC Dpc OPTIONAL //传入上面初始化的DPC对象
);
下面是一个使用的例子
typedef struct _tag_DEVICE_EXTENSION
{
PDEVICE_OBJECT pDeviceObj;
UNICODE_STRING uDeviceName;
UNICODE_STRING uSymbolickName;
KTIMER timer;
KDPC Dpc;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
PDEVICE_EXTENSION pDeviceExtension;
PDEVICE_OBJECT pDeviceObj;
int i;
NTSTATUS status;
LARGE_INTEGER time_out;
UNREFERENCED_PARAMETER(pRegistryPath);
pDriverObject->DriverUnload = DriverUnload;
//设置派遣函数
...
status = CreateDevice(pDriverObject, &pDeviceObj);
//失败处理
...
//设置定时器
time_out.QuadPart = -1 * 10000000; //1s = 1000000000ns
status = KeSetTimer(&pDeviceExtension->timer,time_out, &pDeviceExtension->Dpc);
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
//该函数主要用来清理相关资源
...
}
NTSTATUS DefauleDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
//默认返回成功
}
NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObj, PDEVICE_OBJECT *ppDeviceObj)
{
PDEVICE_EXTENSION pDevEx;
PDEVICE_OBJECT pDevObj;
UNICODE_STRING uDeviceName;
UNICODE_STRING uSymbolicName;
NTSTATUS status;
//创建设备对象,填充扩展设备内容
...
//初始化KTIMER DPC
KeInitializeTimer(&pDevEx->timer);
KeInitializeDpc(&pDevEx->Dpc, TimerDpc, pDevObj);
//设置连接符号
...
return STATUS_SUCCESS;
}
VOID TimerDpc(
__in struct _KDPC *Dpc,
__in_opt PVOID DeferredContext,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2
)
{
static int i = 0;
PTSTR pProcessName;
PEPROCESS pEprocess;
LARGE_INTEGER time_out;
PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)DeferredContext;
PDEVICE_EXTENSION pDevEx = (PDEVICE_EXTENSION)(pDevObj->DeviceExtension);
ASSERT(NULL != pDevObj);
pEprocess = PsGetCurrentProcess();
pProcessName = (PTSTR)((ULONG)pEprocess + 0x174);
DbgPrint("%d Call TimerDpc, Process: %s\n", i, pProcessName);
time_out.QuadPart = -1 * 10000000; //1s = 1000000000ns
KeSetTimer(&pDevEx->timer, time_out, &pDevEx->Dpc);
i++;
}