内核与驱动_06_时间与定时器

获取当前“滴答”数

  • 获取系统日期和时间往往是为了写日志,获得启动毫秒数则常用来做随机数种子。

  • 有时也使用时间相关的函数来寻找程序的性能瓶颈。

  • 在内核中有一个函数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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值