內核與驅動_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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章