获取当前“滴答”数
-
获取系统日期和时间往往是为了写日志,获得启动毫秒数则常用来做随机数种子。
-
有时也使用时间相关的函数来寻找程序的性能瓶颈。
-
在内核中有一个函数
KeQueryTickCount
,返回系统自启动之后经历的毫秒数。
VOID KeQueryTickCount(
OUT PLARGE_INTEGER TickCount
);
- 严格来说获取到的是一个“滴答”数,在不同的硬件环境中可能有所不同。所以需要配合一个函数:
ULONG KeQueryTimeIncrement();
来使用,他可以用来获得一个“滴答”的具体的100纳秒数。
//函数用来获得实际的毫秒数
void MyGetTickCount(PULONG msec)
{
LARGE_INTEGER tickCount;
ULONG myInc=KeQueryTimeIncrement();
KeQueryTickCount(&tickCount);
tickCount.QuadPart*=myinc;
tickCount.QuadPart/=10000;
*msec=tickCount.LowPart;
}
获取当前系统时间
- 在驱动中没有了诸如CTime之类的类,不过加入了一个
TIME_FIELDS
这个结构。
typedef struct _TIME_FIELDS {
CSHORT Year; // range [1601...]
CSHORT Month; // range [1..12]
CSHORT Day; // range [1..31]
CSHORT Hour; // range [0..23]
CSHORT Minute; // range [0..59]
CSHORT Second; // range [0..59]
CSHORT Milliseconds;// range [0..999]
CSHORT Weekday; // range [0..6] == [Sunday..Saturday]
} TIME_FIELDS;
typedef TIME_FIELDS *PTIME_FIELDS;
- 可以使用
KeQuerySystemTime
获得格林威治时间,然后使用ExSystemTimeToLocalTime
转换为当地时间
VOID keQuerySystemTime(
OUT PLARG_INTEGER CurrentTime
);
VOID ExSystemTimeToLocalTime(
IN PLARGE_INTEGER SystemTime,
OUT PLARGER_INTEGER LocalTime
);
//上述两个函数使用的结构是长长整型,必须通过RtlTimeToTimeFields转换为TIME_FIELDS
VOID RtlTimeToTimeFields(
IN PLAGE_INTEGER Time,
OUT PTIME_FIELDS TimeFields
);
使用示例
//函数用来获取当前时间,并格式化打印
VOID MyCurTimeStr()
{
LARGE_INTEGER sysTime, localTime;
//获取系统时间
KeQuerySystemTime(&sysTime);
//转换为本地时间
ExSystemTimeToLocalTime(&sysTime, &localTime);
//转换为可读的时间格式
TIME_FIELDS strTime = { 0 };
RtlTimeToTimeFields(&localTime, &strTime);
//打印
KdPrint(("%04d-%02d-%02d %02d:%02d:%02d\n", strTime.Year, strTime.Month, strTime.Day, strTime.Hour, strTime.Minute, strTime.Second));
}
定时器
- 在驱动开发中有类似与
SetTimer
的函数来实现定时器功能,这是一种经典的方法。
BOOLEAN keSetTimer(
IN PKTIMER Timer, //定时器
IN LARGE_INTEGER, //延后执行的时间
IN PKDPC Dpc OPTIONAL //要执行的回调函数结构体
);
- 定时器Timer可以使用下述代码初始化:
KTIMER timer;
KeInitializeTimer(&timer);
- 至于回调函数结构初始化比较麻烦,需要提供一个回调函数,初始化Dpc的函数原型如下:
VOID KeInitializeDpc(
IN PRKDPC Dpc,
IN PKDEFERRED_ROUTINE deferredRoutine,
IN PVOID DeferredContext
);
PKDEFERRED_ROUTINE
这个函数指针类型所对应的函数类型如下:
VOID CustomDpc(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
//重要参数:DeferredContext,它是KeInitializeDpc调用时传入的参数,用户可以通过此参数传入一些数据
书中封装的定时器
//内部时钟结构
typedef struct _MY_TIMER
{
KDPC dpc;
KTIMER timer;
PKDEFERRED_ROUTINE func;
PVOID private_context;
}MY_TIMER,*PMY_TIMER;
//初始化这个结构
void MyTimerInit(PMY_TIMER timer,PKDEFERRED_ROUTINE func)
{
KeInitializeDpc(&timer->dpc,sf_my_dpc_routine,timer);
timer->func=func;
KeinitializeTimer(&timer->timer);
}
//让结构中的回调函数在n毫秒之后开始运行
BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context)
{
//due是一个64位时间值,为正表示从1601.1.1日算起,为负表示它是相对于当前时间的一段时间间隔
LARGE_INTEGER due;
//这里注意时间单位的转换,这里msec是毫秒
due.QuadPart=-10000*msec;
//用户私有上下文
timer->private_context=context;
return KesetTimer(&timer->timer,due,&mytimer->dpc);
}
//停止执行
VOID MyTimerDestroy(PMY_TIMER timer)
{
KeCancelTimer(&mytimer->timer);
}
//要注意要在真正的MyOnTimer回调函数中要获得上下文选哟从timer->private_context中获得,此外在MyOnTimer中还有必要再次调用MyTimerSet来保证下次依然得到执行
VOID MyOnTimer(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
In PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
//这里传入的上下文是timer结构,用来下次在启动延时调用
PMY_TIMER timer=(PMY_TIMER)DeferredContext;
//获得用户上下文
PVOID my_context=timer->private_context;
//可以任意操作
....
//再次手动调用
MyTimerSet(timer,1000,my_context);
}