首页 > 学院 > 开发设计 > 正文

定时器的实现

2019-11-08 18:34:29
字体:
来源:转载
供稿:网友

使用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 LOCKEDCODEVOID 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 INITCODENTSTATUS 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++;}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表