9、Windows驱动开发技术详解笔记(5) 基本语法回顾
?
?
5、在驅(qū)動(dòng)中獲取系統(tǒng)時(shí)間1)獲取啟動(dòng)毫秒數(shù)
?在ring3 我們可以通過一個(gè)GetTickCount 函數(shù)來獲得自系統(tǒng)啟動(dòng)開始的毫秒數(shù),在ring0也有一個(gè)與之對(duì)應(yīng)的KeQueryTickCount 函數(shù)。不幸的是,這個(gè)函數(shù)并不能直接返回毫秒數(shù),它返回的是“滴答”數(shù),而一個(gè)時(shí)鐘“滴答”到底是多久,這在不同的系統(tǒng)中可能是不同的,因此我們還需要另外一個(gè)函數(shù)的輔助,即KeQueryTimeIncrement 函數(shù)。KeQueryTimeIncrement 函數(shù)可以返回一個(gè)“滴答”表示多少個(gè)100 納秒,注意這里的單位是100 納秒。
2)獲取系統(tǒng)時(shí)間
在ring3 獲取系統(tǒng)時(shí)間是非常簡(jiǎn)單的,我們直接使用GetLocalTime 就可以通過一個(gè)系統(tǒng)時(shí)間結(jié)構(gòu)體SYSTEMTIME 來返回當(dāng)前時(shí)間。到了ring0我們可以使用KeQuerySystemTime來獲得當(dāng)前時(shí)間,但它其實(shí)是一個(gè)格林威治時(shí)間,與ring3得到的LocalTime 不同,因此我們還需要使用ExSystemTimeToLocalTime
函數(shù)將這個(gè)格林威治時(shí)間轉(zhuǎn)換成當(dāng)?shù)貢r(shí)間。事情到這里還沒有結(jié)束,現(xiàn)在我們獲得的當(dāng)?shù)貢r(shí)間不是一個(gè)容易閱讀的格式,因此我們還要使用RltTimeToTimeFieldh 函數(shù)將其轉(zhuǎn)換成容易閱讀的格式。
3)兩個(gè)小例程
代碼 1 /************************************************************************2
3 * 函數(shù)名稱:MyGetTickCount
4
5 * 功能描述:獲取tick數(shù)目
6
7 * 參數(shù)列表:
8
9 * 返回 值:返回狀態(tài)
10
11 *************************************************************************/
12
13 VOID
14
15 MyGetTickCount()
16
17 {
18
19 LARGE_INTEGER tick_count;
20
21 ULONG inc;
22
23 inc = KeQueryTimeIncrement();
24
25 KeQueryTickCount(&tick_count);
26
27 ?// 因?yàn)? 毫秒等于1000000 納秒,而inc 的單位是100 納秒
28
29 ?// 所以除以10000 即得到當(dāng)前毫秒數(shù)
30 ?
31 tick_count.QuadPart *= inc;
32
33 tick_count.QuadPart /= 10000;
34
35 KdPrint(("[Test] TickCount : %d", tick_count.QuadPart));
36
37 }
38
39 /************************************************************************
40
41 * 函數(shù)名稱:MyGetCurrentTime
42
43 * 功能描述:獲取當(dāng)前系統(tǒng)時(shí)間
44
45 * 參數(shù)列表:
46
47 * 返回 值:返回狀態(tài)
48
49 *************************************************************************/
50
51 VOID
52
53 MyGetCurrentTime()
54
55 {
56
57 LARGE_INTEGER CurrentTime;
58
59 LARGE_INTEGER LocalTime;
60
61 TIME_FIELDS TimeFiled;
62
63 static WCHAR Time_String[32] = {0};
64
65 // 這里得到的其實(shí)是格林威治時(shí)間
66
67 KeQuerySystemTime(&CurrentTime);
68
69 // 轉(zhuǎn)換成本地時(shí)間
70
71 ExSystemTimeToLocalTime(&CurrentTime, &LocalTime);
72
73 // 把時(shí)間轉(zhuǎn)換為容易理解的形式
74
75 RtlTimeToTimeFields(&LocalTime, &TimeFiled);
76
77 KdPrint(("[Test] NowTime : %4d-%2d-%2d %2d:%2d:%2d",
78
79 TimeFiled.Year, TimeFiled.Month, TimeFiled.Day,
80
81 TimeFiled.Hour, TimeFiled.Minute, TimeFiled.Second));
82
83 }
?
4)定時(shí)器
使用定時(shí)器
KeSetTimer()
http://msdn.microsoft.com/en-us/library/ff553286%28VS.85%29.aspx
這個(gè)函數(shù)的原型如下:
BOOLEAN
KeSetTimer(
IN PKTIMER Timer, // 定時(shí)器
IN LARGE_INTEGER DueTime, // 延后執(zhí)行的時(shí)間
IN PKDPC Dpc OPTIONAL // 要執(zhí)行的回調(diào)函數(shù)結(jié)構(gòu)
);
這是因?yàn)樾枰峁┮粋€(gè)回調(diào)函數(shù)。初始化Dpc的函數(shù)原型如下:
VOID
KeInitializeDpc(
IN PRKDPC Dpc,
IN PKDEFERRED_ROUTINE DeferredRoutine,
IN PVOID DeferredContext
);
這是一個(gè)“延時(shí)執(zhí)行”的過程。每次執(zhí)行了后,下次就不會(huì)再被調(diào)用了。如果想要定時(shí)反復(fù)執(zhí)行,就必須在每次CustomDpc(DeferredRoutine)函數(shù)被調(diào)用的時(shí)候,再次調(diào)用KeSetTimer,來保證下次還可以執(zhí)行。
注意的是,CustomDpc將運(yùn)行在APC中斷級(jí)。因此并不是所有的事情都可以做(在調(diào)用任何內(nèi)核系統(tǒng)函數(shù)的時(shí)候,請(qǐng)注意WDK說明文檔中標(biāo)明的中斷級(jí)要求。)因此要完全實(shí)現(xiàn)定時(shí)器的功能,需要自己封裝一些東西。下面的結(jié)構(gòu)封裝了全部需要的信息:
// 內(nèi)部時(shí)鐘結(jié)構(gòu)
typedef struct MY_TIMER_
{
KDPC dpc;
KTIMER timer;
PKDEFERRED_ROUTINE func;
PVOID private_context;
} MY_TIMER,*PMY_TIMER;
代碼 1 // 初始化這個(gè)結(jié)構(gòu):
2
3 void MyTimerInit(PMY_TIMER timer, PKDEFERRED_ROUTINE func)
4
5 {
6
7 // 請(qǐng)注意,我把回調(diào)函數(shù)的上下文參數(shù)設(shè)置為timer,為什么要
8
9 // 這樣做呢?
10
11 KeInitializeDpc(&timer->dpc,sf_my_dpc_routine,timer);
12
13 timer->func = func;
14
15 KeInitializeTimer(&timer->timer);
16
17 return (wd_timer_h)timer;
18
19 }
20
21 // 讓這個(gè)結(jié)構(gòu)中的回調(diào)函數(shù)在n毫秒之后開始運(yùn)行:
22
23 BOOLEAN MyTimerSet(PMY_TIMER timer,ULONG msec,PVOID context)
24
25 {
26
27 LARGE_INTEGER due;
28
29 // 注意時(shí)間單位的轉(zhuǎn)換。這里msec是毫秒。
30
31 due.QuadPart = -10000*msec;
32
33 // 用戶私有上下文。
34
35 timer->private_context = context;
36
37 return KeSetTimer(&timer->timer,due,&mytimer->dpc);
38
39 };
40
41 // 停止執(zhí)行
42
43 VOID MyTimerDestroy(PMY_TIMER timer)
44
45 {
46
47 KeCancelTimer(&mytimer->timer);
48
49 };
?
使用結(jié)構(gòu)PMY_TIMER已經(jīng)比結(jié)合使用KDPC和KTIMER簡(jiǎn)便許多。但是還是有一些要注意的地方。真正的OnTimer回調(diào)函數(shù)中,要獲得上下文,必須要從timer->private_context中獲得。此外,OnTimer中還有必要再次調(diào)用MyTimerSet(),來保證下次依然得到執(zhí)行。
VOID
MyOnTimer (
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
// 這里傳入的上下文是timer結(jié)構(gòu),用來下次再啟動(dòng)延時(shí)調(diào)用
PMY_TIMER timer = (PMY_TIMER)DeferredContext;
// 獲得用戶上下文
PVOID my_context = timer->private_context;
// 在這里做OnTimer中要做的事情
……
// 再次調(diào)用。這里假設(shè)每1秒執(zhí)行一次
MyTimerSet(timer,1000,my_context);
};
6、在驅(qū)動(dòng)中創(chuàng)建內(nèi)核線程
1)創(chuàng)建
在ring3 我們可以使用CreateThread這個(gè)Win32 API 創(chuàng)建線程,在ring0也有與之對(duì)應(yīng)的內(nèi)核函數(shù)PsCreateSystemThread。
這個(gè)函數(shù)與CreateThread的使用很相似,它可以通過第一個(gè)參數(shù)返回線程的句柄,最后兩個(gè)參數(shù)分別指定線程函數(shù)的地址和參數(shù),在ring3我們就是這么做的。
我們使用CreateThread創(chuàng)建的線程只屬于當(dāng)前進(jìn)程(不過CreateRemoteThread函數(shù)可以在指定進(jìn)程中創(chuàng)建線程),而PsCreateSystemThread 函數(shù)默認(rèn)情況下創(chuàng)建的卻是一個(gè)系統(tǒng)進(jìn)程,它屬于進(jìn)程名為“system”,PID=4的這個(gè)進(jìn)程。不過PsCreateSystemThread也是可以創(chuàng)建用戶線程的,這取決于它的第四個(gè)參數(shù)ProcessHandle,如果它為空,則創(chuàng)建的即系統(tǒng)線程;如果它是一個(gè)進(jìn)程句柄,則創(chuàng)建的就是屬于該指定進(jìn)程的用戶線程。
線程函數(shù)是一個(gè)非常重要的部分,它決定了該線程具有什么樣的功能。線程函數(shù)必須按照如下規(guī)范聲明:
VOID ThreadProc(IN PVOID context);
這個(gè)VOID指針參數(shù)通過強(qiáng)制轉(zhuǎn)換可以達(dá)到很多特殊效果,給予了我們很大的自由度。我們還需要注意的一點(diǎn),在內(nèi)核里創(chuàng)建的線程必須自己調(diào)用PsTerminateSystemThread 來結(jié)束自身,它不能像ring3 的線程那樣可以在執(zhí)行完畢后自動(dòng)結(jié)束。
NTSTATUS
PsCreateSystemThread(
OUT PHANDLE ThreadHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle OPTIONAL,
OUT PCLIENT_ID ClientId OPTIONAL,
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext);
這個(gè)函數(shù)的參數(shù)也很多。經(jīng)驗(yàn)如下:ThreadHandle用來返回句柄。放入一個(gè)句柄指針即可。DesiredAccess總是填寫0。后面三個(gè)參數(shù)都填寫NULL。最后的兩個(gè)參數(shù)一個(gè)用于改線程啟動(dòng)的時(shí)候執(zhí)行的函數(shù)。一個(gè)用于傳入該函數(shù)的參數(shù)。
2)線程同步
雖然多線程并不是真正的并發(fā)運(yùn)行,但由于CPU分配的時(shí)間片很短,看起來它們就像是并發(fā)運(yùn)行的一樣。
此前我們?cè)?jīng)介紹過自旋鎖,它就是一種典型的同步方案,不過在線程同步的時(shí)候通常不使用它,而是使用事件通知,此外還有類似ring3的臨界區(qū)、信號(hào)燈等方法。
下面我們介紹使用KEVENT事件對(duì)象進(jìn)行同步的方法。
在使用KEVENT 事件對(duì)象前,需要首先調(diào)用內(nèi)核函數(shù)KeInitialize Event 對(duì)其初始化,這
個(gè)函數(shù)的原型如下所示:
VOID
KeInitializeEvent(
IN PRKEVENT Event,
IN EVENT_TYPE Type,
IN BOOLEAN State);
第一個(gè)參數(shù)Event 是初始化事件對(duì)象的指針;第二個(gè)參數(shù)Type表明事件的類型。事件分兩種類型:一類是“通知事件”,對(duì)應(yīng)參數(shù)為NotificationEvent,另一類是“同步事件”,對(duì)應(yīng)參數(shù)為SynchronizationEvent;第三個(gè)參數(shù)State 如果為TRUE,則事件對(duì)象的初始化狀態(tài)為激發(fā)狀態(tài),否則為未激發(fā)狀態(tài)。
如果創(chuàng)建的事件對(duì)象是“通知事件”,當(dāng)事件對(duì)象變?yōu)榧ぐl(fā)態(tài)時(shí),需要我們手動(dòng)將其改回未激發(fā)態(tài)。如果創(chuàng)建的事件對(duì)象是“同步事件”,當(dāng)事件對(duì)象為激發(fā)態(tài)時(shí),如果遇到相應(yīng)的KeWaitForXXXX 等內(nèi)核函數(shù),事件對(duì)象會(huì)自動(dòng)變回到未激發(fā)態(tài)。設(shè)置事件的函數(shù)是KeSetEvent,可通過該函數(shù)修改事件對(duì)象的狀態(tài)。
3)一個(gè)例子
代碼 1 KEVENT kEvent;2
3 VOID
4
5 CreateThreadTest()
6
7 {
8
9 HANDLE hThread;
10
11 NTSTATUS status;
12
13 UNICODE_STRING ustrTest;
14
15 // 初始化
16
17 KeInitializeEvent(&kEvent, SynchronizationEvent, TRUE);
18
19 RtlInitUnicodeString(&ustrTest, L"This is a string for test!");
20
21 // 創(chuàng)建系統(tǒng)線程
22
23 status = PsCreateSystemThread(&hThread, 0, NULL, NULL, NULL, MyThreadFunc,
24
25 (PVOID)(&ustrTest));
26
27 if (!NT_SUCCESS(status))
28
29 {
30
31 KdPrint(("[Test] CreateThread Test Failed!"));
32
33 }
34
35 ZwClose(hThread);
36
37 // 等待事件
38
39 KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, 0);
40
41 }
42
43 // 線程函數(shù)
44
45 VOID
46
47 MyThreadFunc( IN PVOID context )
48
49 {
50
51 PUNICODE_STRING str = (PUNICODE_STRING)context;
52
53 KdPrint(("[Test] %d : %wZ", (int)PsGetCurrentProcessId(), str));
54
55 // 設(shè)置事件對(duì)象
56
57 KeSetEvent(&kEvent, 0, FALSE);
58
59 // 結(jié)束線程
60
61 PsTerminateSystemThread(STATUS_SUCCESS);
62
63 }
?
參考
【1】Windows 驅(qū)動(dòng)開發(fā)技術(shù)詳解
【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
【3】Windows驅(qū)動(dòng)學(xué)習(xí)筆記,灰狐
轉(zhuǎn)載于:https://www.cnblogs.com/mydomain/archive/2010/10/18/1855127.html
總結(jié)
以上是生活随笔為你收集整理的9、Windows驱动开发技术详解笔记(5) 基本语法回顾的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wave格式分析,wave音频文件格式分
- 下一篇: 基于AVR和MT8870的远程家电控制系