47、Windows驱动程序模型笔记(五),内存管理
內(nèi)存管理<?xml:namespace prefix = o />
1)內(nèi)核模式與用戶模式地址
圖示 地址空間中用戶模式部分和內(nèi)核模式部分
??? 每個(gè)用戶模式進(jìn)程都有自己的地址上下文,它把用戶模式的虛擬地址映射成一組唯一的物理頁(yè)幀。這意味著,當(dāng)Windows NT調(diào)度器把控制從一個(gè)進(jìn)程的當(dāng)前線程切換到另一個(gè)進(jìn)程的某個(gè)線程時(shí),與進(jìn)程相對(duì)應(yīng)的虛擬地址空間也被更換。線程切換的一個(gè)步驟就是改變處理器當(dāng)前使用的頁(yè)表,以便它能引用新線程的進(jìn)程上下文。
??? 在編寫(xiě)驅(qū)動(dòng)程序時(shí)我們要遵守下面原則:
??? 決不(或幾乎從不)直接引用用戶模式的內(nèi)存地址,無(wú)論何時(shí)我們需要訪問(wèn)計(jì)算機(jī)內(nèi)存,都要使用內(nèi)核模式的虛擬地址。
2)頁(yè)大小
??? 在虛擬內(nèi)存系統(tǒng)中,操作系統(tǒng)以固定大小的頁(yè)幀組織物理內(nèi)存和交換文件。在WDM驅(qū)動(dòng)程序中,常量PAGE_SIZE指出頁(yè)的大小。在某些Windows NT計(jì)算機(jī)中,一頁(yè)有4096字節(jié);在另一些計(jì)算機(jī)中,一頁(yè)有8192字節(jié)。有一個(gè)相關(guān)常量PAGE_SHIFT,你可以從下面語(yǔ)句中看出它的值:
PAGE_SIZE == 1 << PAGE_SHIFT
下面預(yù)處理宏可以簡(jiǎn)化頁(yè)大小的使用:
?ROUND_TO_PAGES 把指定值舍入為下一個(gè)頁(yè)邊界。例如,在4KB頁(yè)的計(jì)算機(jī)上,ROUND_TO_PAGES(1)的結(jié)果為4096,ROUND_TO_PAGES(4097)的結(jié)果為8192。
?BYTES_TO_PAGES 得出給定的字節(jié)量需要多少頁(yè)來(lái)保存。例如,BYTES_TO_PAGES(42)在所有平臺(tái)上都等于1,而B(niǎo)YTES_TO_PAGES(5000)在4KB頁(yè)的平臺(tái)上為2,在8KB頁(yè)的平臺(tái)上為1。
?BYTE_OFFSET 返回虛擬地址的字節(jié)偏移部分。例如,在4KB頁(yè)的計(jì)算機(jī)上,BYTE_OFFSET(0x12345678)的結(jié)果為0x678。
?PAGE_ALIGN 把虛擬地址舍向上一個(gè)頁(yè)邊界。例如,在4KB頁(yè)的計(jì)算機(jī)上,PAGE_ALIGN(0x12345678)的結(jié)果為0x12345000。
?ADDRESS_AND_SIZE_TO_SPAN_PAGES 返回從指定虛擬地址開(kāi)始的指定字節(jié)數(shù)所跨過(guò)的頁(yè)數(shù)。例如,在4KB的計(jì)算機(jī)上, ADDRESS_AND_SIZE_TO_SPAN_PAGES(0x12345FFF,2)的結(jié)果為2,因?yàn)檫@兩個(gè)字節(jié)跨過(guò)了頁(yè)邊界。
3)Windows NT把內(nèi)核模式地址空間分成分頁(yè)內(nèi)存池和非分頁(yè)內(nèi)存池。(用戶模式地址空間總是分頁(yè)的) 必須駐留的代碼和數(shù)據(jù)放在非分頁(yè)池;不必常駐的代碼和數(shù)據(jù)放在分頁(yè)池中。
執(zhí)行在高于或等于DISPATCH_LEVEL級(jí)的代碼不可以引發(fā)頁(yè)故障。
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGE")
#endif
data_seg編譯指示使所有在其后聲明的靜態(tài)數(shù)據(jù)變量進(jìn)入分頁(yè)池。這個(gè)編譯指示與alloc_text完全不同。一個(gè)分頁(yè)段可以從#pragma data_seg("PAGE")出現(xiàn)的地方開(kāi)始到#pragma data_seg()出現(xiàn)的地方結(jié)束。而Alloc_text僅應(yīng)用于單個(gè)函數(shù)。
??? 為了檢測(cè)編譯器是否是Microsoft的編譯器,可以測(cè)試預(yù)定義宏_MSC_VER是否存在。
掉電期間是釋放鎖定內(nèi)存頁(yè)的最佳時(shí)期。
| 服務(wù)函數(shù) | 描述 |
| MmLockPagableCodeSection | 鎖定含有給定地址的代碼段 |
| MmLockPagableDataSection | 鎖定含有給定地址的數(shù)據(jù)段 |
| MmLockPagableSectionByHandle | 用MmLockPagableCodeSection返回的句柄鎖定代碼段(僅用于Windows 2000) |
| MmPageEntireDriver | 解鎖所有屬于某驅(qū)動(dòng)程序的頁(yè) |
| MmResetDriverPaging | 恢復(fù)整個(gè)驅(qū)動(dòng)程序的編譯時(shí)分頁(yè)屬性 |
| MmUnlockPagableImageSection | 為一個(gè)鎖定代碼段或數(shù)據(jù)段解鎖 |
| 服務(wù)函數(shù)或宏 | 描述 |
| PushEntryList | 向鏈表頂加入元素 |
| PopEntryList | 刪除最上面的元素 |
4)把C/C++編程規(guī)范引入驅(qū)動(dòng)編程中
如RemoveHeadList是一個(gè)宏,如果if語(yǔ)句中不用{},則出問(wèn)題。
5)圖示顯示了 lookaside鏈表的概念。假設(shè)有一個(gè)可以在水池中直上直下平衡的玻璃杯子。這個(gè)杯子就代表lookaside鏈表對(duì)象。當(dāng)初始化該對(duì)象時(shí),你告訴系 統(tǒng)需要多大的內(nèi)存塊(杯中的水滴)。在早期版本的Windows NT中,你還要指出杯子的容量,但現(xiàn)在的操作系統(tǒng)可以自動(dòng)適應(yīng)。為了滿足一個(gè)內(nèi)存分配請(qǐng)求,系統(tǒng)首先嘗試著從鏈表中取出(刪除)一塊內(nèi)存(從杯子中取出一 滴水)。如果連一塊內(nèi)存也沒(méi)有,系統(tǒng)就從外面內(nèi)存池中取。相反,釋放內(nèi)存時(shí),系統(tǒng)首先嘗試著放到鏈表上(向杯子中加入一滴水)。但是,如果鏈表滿了,那么 這個(gè)內(nèi)存塊就返回到外界的內(nèi)存池中(杯子中的水溢出到水池)。
圖示. Lookaside鏈表
4、字符串
String Manipulation
http://msdn.microsoft.com/en-us/library/ee479680.aspx
Buffer Manipulation
http://msdn.microsoft.com/en-us/library/ee479504.aspx
表示 P89串處理函數(shù)
| 操作 | ANSI串函數(shù) | Unicode串函數(shù) |
| Length | strlen | wcslen |
| Concatenate | strcat, strncat | wcscat, wcsncat, RtlAppendUnicodeStringToString, RtlAppendUnicodeToString |
| Copy | strcpy, strncpy, RtlCopyString | wcscpy, wcsncpy, RtlCopyUnicodeString |
| Reverse | _strrev | _wcsrev |
| Compare | strcmp, strncmp, _stricmp, _strnicmp, RtlCompareString, RtlEqualString | wcscmp, wcsncmp, _wcsicmp, _wcsnicmp, RtlCompareUnicodeString, RtlEqualUnicodeString, RtlPrefixUnicodeString |
| Initialize | _strset, _strnset, RtlInitAnsiString, RtlInitString | _wcsnset, RtlInitUnicodeString |
| Search | strchr, strrchr, strspn, strstr | wcschr, wcsrchr, wcsspn, wcsstr |
| Upper/lowercase | _strlwr, _strupr, RtlUpperString | _wcslwr, _wcsupr, RtlUpcaseUnicodeString |
| Character | isdigit, islower, isprint, isspace, isupper, isxdigit, tolower, toupper, RtlUpperChar | towlower, towupper, RtlUpcaseUnicodeChar |
| Format | sprintf, vsprintf, _snprintf, _vsnprintf | swprintf, _snwprintf |
| String conversion | atoi, atol, _itoa | _itow, RtlIntegerToUnicodeString, RtlUnicodeStringToInteger |
| Type conversion | RtlAnsiStringToUnicodeS ize, RtlAnsiStringToUnicodeS tring | RtlUnicodeStringToAnsiString |
| Memory release | RtlFreeAnsiString | RtlFreeUnicodeString |
1)處理blob數(shù)據(jù)的服務(wù)函數(shù)
表示. 處理blob數(shù)據(jù)的服務(wù)函數(shù)
| 服務(wù)函數(shù)或宏 | 描述 |
| memchr | 在blob中尋找一個(gè)字節(jié) |
| memcpy, RtlCopyBytes, RtlCopyMemory | 復(fù)制字節(jié),不允許重疊 |
| memmove, RtlMoveMemory | 復(fù)制字節(jié),允許重疊 |
| memset, RtlFillBytes, RtlFillMemory | 用給定的值填充blob |
| memcmp, RtlCompareMemory, RtlEqualMemory | 比較兩個(gè)blob |
| memset, RtlZeroBytes, RtlZeroMemory | blob清零 |
?內(nèi)存的“copy”和“move”操作之間的區(qū)別在于可否容忍源和目的相重疊。move操作不管源和目的是否重疊。而copy操作在源和目的有任何重疊時(shí)不工作。
?“byte” 操作和“memory”操作的區(qū)別是操作的間隔尺寸。byte操作保證按字節(jié)為單位執(zhí)行。而memory操作可以在內(nèi)部使用更大的塊,所有這些塊的和等于 指定的字節(jié)數(shù)。這個(gè)區(qū)別會(huì)根據(jù)平臺(tái)的不同而改變,在32位Intel計(jì)算機(jī)上,byte操作實(shí)際上是對(duì)應(yīng)memory操作的宏。但在Alpha平臺(tái) 上,RtlCopyBytes與RtlCopyMemory是完全不同的函數(shù)。
5、注冊(cè)表
| 服務(wù)函數(shù) | 描述 |
| IoOpenDeviceRegistryKey | 打開(kāi)PDO專用鍵 |
| IoOpenDeviceInterfaceRegistryKey | 打開(kāi)與注冊(cè)設(shè)備接口相連的鍵 |
| RtlDeleteRegistryValue | 刪除一個(gè)注冊(cè)表值 |
| RtlQueryRegistryValues | 從注冊(cè)表中讀取多個(gè)值 |
| RtlWriteRegistryValue | 向注冊(cè)表寫(xiě)一個(gè)值 |
| ZwClose | 關(guān)閉注冊(cè)表鍵句柄 |
| ZwCreateKey | 創(chuàng)建一個(gè)注冊(cè)表鍵 |
| ZwDeleteKey | 刪除一個(gè)注冊(cè)表鍵 |
| ZwEnumerateKey | 枚舉子鍵 |
| ZwEnumerateValueKey | 枚舉某注冊(cè)表鍵中的值 |
| ZwFlushKey | 把注冊(cè)表更改提交到磁盤(pán) |
| ZwOpenKey | 打開(kāi)一個(gè)注冊(cè)表鍵 |
| ZwQueryKey | 取關(guān)于某注冊(cè)表鍵的信息 |
| ZwQueryValueKey | 取某個(gè)注冊(cè)表鍵中的值 |
| ZwSetValueKey | 置某個(gè)注冊(cè)表鍵中的值 |
表示. 注冊(cè)表訪問(wèn)函數(shù)
6、浮點(diǎn)數(shù)
??? 在Intel處理器上,浮點(diǎn)協(xié)處理器還可以執(zhí)行MMX指令。在歷史上,驅(qū)動(dòng)程序在執(zhí)行浮點(diǎn)運(yùn)算上有兩個(gè)問(wèn)題。對(duì)于沒(méi)有浮點(diǎn)協(xié)處理器的計(jì)算機(jī),操作系統(tǒng)將用 軟件仿真一個(gè),但是仿真的浮點(diǎn)協(xié)處理器會(huì)消耗很大的CPU處理能力,并且需要一個(gè)處理器異常來(lái)捕捉浮點(diǎn)指令。在內(nèi)核模式中處理異常,尤其是在提升的 IRQL級(jí)上,是困難的。另外,在有浮點(diǎn)協(xié)處理器的計(jì)算機(jī)上,由于CPU結(jié)構(gòu)上的原因,當(dāng)線程上下文切換時(shí),需要一個(gè)耗時(shí)的操作來(lái)保存和恢復(fù)浮點(diǎn)協(xié)處理器 的狀態(tài)。所以,通常的做法是禁止在內(nèi)核模式驅(qū)動(dòng)程序中使用浮點(diǎn)運(yùn)算。
??? Microsoft建議,除非必要,應(yīng)避免在內(nèi)核模式驅(qū)動(dòng)程序中使用浮點(diǎn)運(yùn)算。
關(guān)于如何使用及更多信息,可以參見(jiàn)《Windows驅(qū)動(dòng)程序模型設(shè)計(jì)》,P99
轉(zhuǎn)載于:https://www.cnblogs.com/mydomain/archive/2011/01/04/1925441.html
總結(jié)
以上是生活随笔為你收集整理的47、Windows驱动程序模型笔记(五),内存管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SGI STL 学习笔记二 vector
- 下一篇: 《Orange’s 一个操作系统的实现》