日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

9、Windows驱动开发技术详解笔记(5) 基本语法回顾

發(fā)布時(shí)間:2025/4/5 windows 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 9、Windows驱动开发技术详解笔记(5) 基本语法回顾 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

?

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í)行,就必須在每次CustomDpcDeferredRoutine)函數(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é)合使用KDPCKTIMER簡(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 }

?

參考

1Windows 驅(qū)動(dòng)開發(fā)技術(shù)詳解

2http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx

3Windows驅(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。