zoukankan      html  css  js  c++  java
  • 定时器的实现

    使用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
    ");
        }
    
        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
    ", i, pProcessName);
    
        time_out.QuadPart = -1 * 10000000; //1s = 1000000000ns
        KeSetTimer(&pDevEx->timer, time_out, &pDevEx->Dpc);
        i++;
    }
  • 相关阅读:
    C# 获取枚举集合的其中两种方式
    UITextField限制字数的方法
    iOS
    iOS
    iOS
    iOS 获取已连接的wifi信息
    AFNetWorking 的简单使用
    CoreData 基本操作方法封装
    在Ios里UIWebView参入js
    AFNetworking教程
  • 原文地址:https://www.cnblogs.com/lanuage/p/7725710.html
Copyright © 2011-2022 走看看