《天书夜读:从汇编语言到windows内核编程》九 时间与定时器

时间:2021-09-08 12:51:40

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

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

  测试代码:

 1 DbgPrint("first: Hello,my salary!"); 
2 ULONG msec1 = 0L;
3 ULONG msec2 = 0L;
4 MyGetTickCount(&msec1);
5 KdPrint(("msec1:%d ms",msec1));
6 for (ULONG i = 0; i< 10000000; i++)
7 {
8 MyGetTickCount(&msec2);
9 }
10 MyGetTickCount(&msec2);
11 KdPrint(("msec2:%d ms",msec2));
12 KdPrint(("SpendTime:%d.%d s",(msec2-msec1)/1000,(msec2-msec1)%1000));

  输出结果:

 

《天书夜读:从汇编语言到windows内核编程》九 时间与定时器

 

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

 1 PWCHAR MyCurTimeStr()
2 {
3 LARGE_INTEGER snow,now;
4 TIME_FIELDS now_fileds;
5 //定义静态变量缓冲区,这里先不考虑多线程隐患
6 static WCHAR time_str[32] = {0};
7 //获取标准时间(格林威治时间)
8 KeQuerySystemTime(&snow);
9 //转换为当地时间
10 ExSystemTimeToLocalTime(&snow,&now);
11 //转换为可理解要素
12 RtlTimeToTimeFields(&now,&now_fileds);
13 //格式化到字符串
14 RtlStringCchPrintfW(time_str,//缓冲区
15 32*2,//缓冲区长度
16 L"%04d-%02d-%02d %02d:%02d:%02d",
17 now_fileds.Year,now_fileds.Month,now_fileds.Day,
18 now_fileds.Hour,now_fileds.Minute,now_fileds.Second);
19 return time_str;
20 }

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

  输出结果:

 

《天书夜读:从汇编语言到windows内核编程》九 时间与定时器

 

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

 

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

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

 1 //内部时钟结构
2 typedef struct MY_TIMER_
3 {
4 KDPC dpc;//dpc
5 KTIMER timer;//定时器
6 LARGE_INTEGER due;//定时毫秒数
7 PKDEFERRED_ROUTINE func;//用户回调函数
8 PVOID privete_context;//用户上下文
9 }MY_TIMER,*PMY_TIMER;
10
11 BOOLEAN MyTimerInit(PMY_TIMER timer,PKDEFERRED_ROUTINE func);
12 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context);
13 BOOLEAN MyTimerDestroy(PMY_TIMER timer);
14 KDEFERRED_ROUTINE MyOnTimer;
15
16 //初始化内部时钟结构:PMY_TIMER,用户回调函数func
17 BOOLEAN MyTimerInit(PMY_TIMER pTimer,PKDEFERRED_ROUTINE func)
18 {
19 if( !pTimer ) return FALSE;
20 //初始化dpc:使用timer->dpc,回调函数TimerProc(内核回调函数)
21 //上下文设置为timer(用于传给用户回调函数)
22 KeInitializeDpc(&pTimer->dpc,(PKDEFERRED_ROUTINE)TimerProc,pTimer);
23 pTimer->func = func;//设置用户自定义回调函数
24 //初始化定时器
25 KeInitializeTimer(&pTimer->timer);
26 return TRUE;
27 }
28
29 //让内部时钟结构体的回调函数在n毫秒后执行:PMY_TIMER,定时毫秒数msec,用户上下文context
30 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context)
31 {
32 //定时时间值转换
33 //为正时指定相对系统的间隔时间:受系统时间设置影响
34 //为负则指定绝对系统时间:为纳秒数,不受系统时间设置影响
35 timer->due.QuadPart = -10000*(LONG)msec;//必须转为有符号类型
36 //用户私有上下文
37 timer->privete_context = context;
38 //添加定时器到系统定时器队列
39 return KeSetTimer(&timer->timer,timer->due,&timer->dpc);
40 }
41
42 //停止执行
43 BOOLEAN MyTimerDestroy(PMY_TIMER pTimer)
44 {
45 if(pTimer) return KeCancelTimer(&pTimer->timer);
46 else return FALSE;
47 }
48
49 //定时器回调函数:内核使用
50 void TimerProc(struct _KDPC *Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)
51 {
52 //这里传入的上下文是timer结构,用来下次再启动延时调用
53 PMY_TIMER pTimer = (PMY_TIMER)DeferredContext;
54
55 if(pTimer->func)//用户回调函数
56 {
57 PKDEFERRED_ROUTINE pFunc = (PKDEFERRED_ROUTINE)pTimer->func;//取得用户定义回调函数指针
58 pFunc(Dpc,(PVOID)pTimer->privete_context,SystemArgument1,SystemArgument2);//调用用户定义的回调函数
59 }
60 else KdPrint(("No callback function !\n"));
61 KeSetTimer(&pTimer->timer,pTimer->due,&pTimer->dpc);//再次设置定时器
62 }

  测试代码:

 1 #define SIZE 30
2 //用户上下文(自定义结构体)
3 typedef struct
4 {
5 WCHAR message[SIZE];
6 ULONG times;
7 }USER_CONTENT,*PUSER_CONTENT;
8
9 //用户回调函数
10 KDEFERRED_ROUTINE TimerProc;
11
12 //定时器回调函数:用户实现(函数名可更改)
13 void MyOnTimer(struct _KDPC *Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)
14 {
15 //在这里做OnTimer中要做的事,封装的好处就是编码时只关注自己想关注的东西
16 PUSER_CONTENT pMyContext = (PUSER_CONTENT)DeferredContext;
17 pMyContext->times++;
18 KdPrint(("第%d次调用定时器:%ws",pMyContext->times,pMyContext->message));
19 }

  过程:

 1 MY_TIMER timer;
2 USER_CONTENT MyContent;
3 ULONG i = 0;
4
5 //用户自定义结构
6 RtlStringCbPrintfW((NTSTRSAFE_PWSTR)&MyContent.message,SIZE*sizeof(WCHAR),L"Hello Timer!");//格式串
7 MyContent.times = 0;//计数值
8
9 //初始化定时器
10 MyTimerInit(&timer,(PKDEFERRED_ROUTINE)MyOnTimer);
11
12 //启动定时器
13 MyTimerSet(&timer,1000,(PVOID)&MyContent);
14
15 //等待一段时间
16 while ( ++i < 4*10E8 );
17
18 //关闭定时器
19 MyTimerDestroy(&timer);
20 KdPrint(("定时器已经停止!"));

  效果图:

《天书夜读:从汇编语言到windows内核编程》九 时间与定时器