獲取當前“滴答”數
-
獲取系統日期和時間往往是爲了寫日誌,獲得啓動毫秒數則常用來做隨機數種子。
-
有時也使用時間相關的函數來尋找程序的性能瓶頸。
-
在內核中有一個函數
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);
}