1)使用如下自定义函数获取自系统启动后经历的毫秒数:KeQueryTimeIncrement、KeQueryTickCount

 void MyGetTickCount(PULONG msec)
 {
     LARGE_INTEGER tick_count;
     ULONG myinc = KeQueryTimeIncrement();
     //返回每次时钟中断,系统时间增加的纳秒数
     KeQueryTickCount(&tick_count);
     //返回自系统启动以来经历的定时器中断次数
     tick_count.QuadPart *= myinc;
     //计算从系统启动来经历多的总纳秒数(纳秒为一个单位)
     tick_count.QuadPart /= ;
     //转换为毫秒数(/10则取微秒)
     *msec = tick_count.LowPart;
     //取低字节(也可以取整个LAGER_INTEGER,改变传入参数)
 }

  测试代码:

 DbgPrint("first: Hello,my salary!");
 ULONG msec1 = 0L;
 ULONG msec2 = 0L;
 MyGetTickCount(&msec1);
 KdPrint(("msec1:%d ms",msec1));
 ; i< ; i++)
 {
     MyGetTickCount(&msec2);
 }
 MyGetTickCount(&msec2);
 KdPrint(("msec2:%d ms",msec2));
 KdPrint((,(msec2-msec1)%));

  输出结果:

2)获取当前系统时间:KeQuerySystemTime、ExSystemTimeToLocalTime、RtlTimeToTimeFields

 PWCHAR MyCurTimeStr()
 {
     LARGE_INTEGER snow,now;
     TIME_FIELDS now_fileds;
     //定义静态变量缓冲区,这里先不考虑多线程隐患
     ] = {};
     //获取标准时间(格林威治时间)
     KeQuerySystemTime(&snow);
     //转换为当地时间
     ExSystemTimeToLocalTime(&snow,&now);
     //转换为可理解要素
     RtlTimeToTimeFields(&now,&now_fileds);
     //格式化到字符串
     RtlStringCchPrintfW(time_str,//缓冲区
         *,//缓冲区长度
         L"%04d-%02d-%02d %02d:%02d:%02d",
         now_fileds.Year,now_fileds.Month,now_fileds.Day,
         now_fileds.Hour,now_fileds.Minute,now_fileds.Second);
     return time_str;
 }

  测试代码:KdPrint(("%ws",MyCurTimeStr()));

  输出结果:

3)中断级:内核代码都运行在一定的中断级上,从微观上看代码的运行并不连续(异常发生、中断发生、线程切换等)。在高中断级运行的代码不会被在低中断级运行的代码打断,帮助文档对每个API应该在什么中断级范围内使用均有说明,中断级高低为:Dispatch>APC>Passive。

4)使用定时器:KeSetTimer、KeInitializeTimer、KeInitializeDpc、CustomDpc、KeCancelTimer

  定时器的CustomDpc运行在APC中断级,所以定时器并不是可以用来定时做任何事情。

 //内部时钟结构
 typedef struct MY_TIMER_
 {
     KDPC dpc;//dpc
     KTIMER timer;//定时器
     LARGE_INTEGER due;//定时毫秒数
     PKDEFERRED_ROUTINE func;//用户回调函数
     PVOID privete_context;//用户上下文
 }MY_TIMER,*PMY_TIMER;

 BOOLEAN MyTimerInit(PMY_TIMER timer,PKDEFERRED_ROUTINE func);
 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context);
 BOOLEAN MyTimerDestroy(PMY_TIMER timer);
 KDEFERRED_ROUTINE MyOnTimer;

 //初始化内部时钟结构:PMY_TIMER,用户回调函数func
 BOOLEAN MyTimerInit(PMY_TIMER pTimer,PKDEFERRED_ROUTINE func)
 {
     if( !pTimer ) return FALSE;
     //初始化dpc:使用timer->dpc,回调函数TimerProc(内核回调函数)
     //上下文设置为timer(用于传给用户回调函数)
     KeInitializeDpc(&pTimer->dpc,(PKDEFERRED_ROUTINE)TimerProc,pTimer);
     pTimer->func = func;//设置用户自定义回调函数
     //初始化定时器
     KeInitializeTimer(&pTimer->timer);
     return TRUE;
 }

 //让内部时钟结构体的回调函数在n毫秒后执行:PMY_TIMER,定时毫秒数msec,用户上下文context
 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context)
 {
     //定时时间值转换
     //为正时指定相对系统的间隔时间:受系统时间设置影响
     //为负则指定绝对系统时间:为纳秒数,不受系统时间设置影响
     timer->due.QuadPart = -*(LONG)msec;//必须转为有符号类型
     //用户私有上下文
     timer->privete_context = context;
     //添加定时器到系统定时器队列
     return KeSetTimer(&timer->timer,timer->due,&timer->dpc);
 }

 //停止执行
 BOOLEAN MyTimerDestroy(PMY_TIMER pTimer)
 {
     if(pTimer) return KeCancelTimer(&pTimer->timer);
     else return FALSE;
 }

 //定时器回调函数:内核使用
 void TimerProc(struct _KDPC *Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)
 {
     //这里传入的上下文是timer结构,用来下次再启动延时调用
     PMY_TIMER pTimer = (PMY_TIMER)DeferredContext;

     if(pTimer->func)//用户回调函数
         {
         PKDEFERRED_ROUTINE pFunc = (PKDEFERRED_ROUTINE)pTimer->func;//取得用户定义回调函数指针
         pFunc(Dpc,(PVOID)pTimer->privete_context,SystemArgument1,SystemArgument2);//调用用户定义的回调函数
     }
     else KdPrint(("No callback function !\n"));
     KeSetTimer(&pTimer->timer,pTimer->due,&pTimer->dpc);//再次设置定时器
 }

  测试代码:

 #define SIZE 30
 //用户上下文(自定义结构体)
 typedef struct
 {
     WCHAR message[SIZE];
     ULONG times;
 }USER_CONTENT,*PUSER_CONTENT;

 //用户回调函数
 KDEFERRED_ROUTINE TimerProc;

 //定时器回调函数:用户实现(函数名可更改)
 void MyOnTimer(struct _KDPC *Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)
 {
     //在这里做OnTimer中要做的事,封装的好处就是编码时只关注自己想关注的东西
     PUSER_CONTENT pMyContext = (PUSER_CONTENT)DeferredContext;
     pMyContext->times++;
     KdPrint(("第%d次调用定时器:%ws",pMyContext->times,pMyContext->message));
 }

  过程:

 MY_TIMER timer;
 USER_CONTENT MyContent;
 ULONG i = ;

 //用户自定义结构
 RtlStringCbPrintfW((NTSTRSAFE_PWSTR)&MyContent.message,SIZE*sizeof(WCHAR),L"Hello Timer!");//格式串
 MyContent.times = ;//计数值

 //初始化定时器
 MyTimerInit(&timer,(PKDEFERRED_ROUTINE)MyOnTimer);

 //启动定时器
 MyTimerSet(&timer,,(PVOID)&MyContent);

 //等待一段时间
 *10E8 );

 //关闭定时器
 MyTimerDestroy(&timer);
 KdPrint(("定时器已经停止!"));

  效果图:

《天书夜读:从汇编语言到windows内核编程》九 时间与定时器的更多相关文章

  1. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...

  2. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  3. 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

    1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...

  4. 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序

    ---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...

  5. 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建

    1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...

  6. 《天书夜读:从汇编语言到windows内核编程》十 线程与事件

    1)驱动中使用到的线程是系统线程,在system进程中.创建线程API函数:PsCreateSystemThread:结束线程(线程内自行调用)API函数:PsTerminateSystemThrea ...

  7. 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存

    1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...

  8. 《天书夜读:从汇编语言到windows内核编程》三 练习反汇编C语言程序

    1) Debug版本算法反汇编,现有如下3×3矩阵相乘的程序: #define SIZE 3 int MyFunction(int a[SIZE][SIZE],int b[SIZE][SIZE],in ...

  9. 《天书夜读:从汇编语言到windows内核编程》二 C语言的流程与处理

    1) Debug与Release的区别:前者称调试版,后者称发行版.调试版基本不优化,而发行版会经过编译器的极致优化,往往与优化前的高级语言执行流程会大相径庭,但是实现的功能是等价的. 2) 如下fo ...

随机推荐

  1. WPF 学习笔记 路由事件

    1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向 ...

  2. hiveQL去重

    去重: 以id进行分组,然后取出每组的第一个 ; 以id进行分组,按照create_time降序排序后,然后取出每组的第一个 ; 将去重后的数据重新存储 ; 去重之后与其他表join算匹配数 sele ...

  3. 根据Dockerfile创建docker dotnet coer 镜像

    那我们先来看看Dockerfile文件内容,注意这个文件是没后缀名的. #依赖原始的镜像,因为我们是要创建dotnet coer镜像,所以我就用了官方给的镜像[microsoft/dotnet:lat ...

  4. LeetCode 63. Unique Path II(所有不同路径之二)

    Follow up for "Unique Paths": Now consider if some obstacles are added to the grids. How m ...

  5. maven 集成tomcat6,tomcat7

    1. maven 集成 tomcat6的配置 maven自带的是tomcat6插件,所以不配置的话也可以,默认tomcat6,8080端口,需要更改端口或者编码方式等,也可以自己再配置一次: < ...

  6. 微软Tech Summit 2017,微软携手Unity打造MR之夜

    2017年10月31日至11月3日,微软将在北京举办Tech Summit 2017技术暨生态大会.今年的大会不仅有大咖级人物带来的十二大主题.百余场课程,而且还会迎来最特别的一位嘉宾--微软公司首席 ...

  7. [http服务]

    [http服务] CentOS 6 httpd 程序环境 记录了httpd的主进程编号: v 主程序文件: /usr/sbin/httpd /usr/sbin/httpd.worker /usr/sb ...

  8. 浅析Entity Framework Core中的并发处理

    前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...

  9. UWP 磁贴设置

    一:需求 一款好看好用的应用,对于UWP来说,动态的磁贴必不可少. 二:TileUpdateManager类 和TileUpdater类 如果需要更改或更新应用的磁贴,那么首先需要获得TileUpda ...

  10. IntelliJ IDEA创建多模块依赖项目

    刚从Eclipse转IDEA, 所以记录一下IDEA的使用 创建多模块依赖项目 1. 新建父工程 这样就创建好了一个普通项目,一般我们会把src删掉,在此项目下新建新的模块 2. 新建子模块 创建供前 ...