47、Windows驱动程序模型笔记(五),内存管理
內存管理<?xml:namespace prefix = o />
1)內核模式與用戶模式地址
圖示 地址空間中用戶模式部分和內核模式部分
??? 每個用戶模式進程都有自己的地址上下文,它把用戶模式的虛擬地址映射成一組唯一的物理頁幀。這意味著,當Windows NT調度器把控制從一個進程的當前線程切換到另一個進程的某個線程時,與進程相對應的虛擬地址空間也被更換。線程切換的一個步驟就是改變處理器當前使用的頁表,以便它能引用新線程的進程上下文。
??? 在編寫驅動程序時我們要遵守下面原則:
??? 決不(或幾乎從不)直接引用用戶模式的內存地址,無論何時我們需要訪問計算機內存,都要使用內核模式的虛擬地址。
2)頁大小
??? 在虛擬內存系統中,操作系統以固定大小的頁幀組織物理內存和交換文件。在WDM驅動程序中,常量PAGE_SIZE指出頁的大小。在某些Windows NT計算機中,一頁有4096字節;在另一些計算機中,一頁有8192字節。有一個相關常量PAGE_SHIFT,你可以從下面語句中看出它的值:
PAGE_SIZE == 1 << PAGE_SHIFT
下面預處理宏可以簡化頁大小的使用:
?ROUND_TO_PAGES 把指定值舍入為下一個頁邊界。例如,在4KB頁的計算機上,ROUND_TO_PAGES(1)的結果為4096,ROUND_TO_PAGES(4097)的結果為8192。
?BYTES_TO_PAGES 得出給定的字節量需要多少頁來保存。例如,BYTES_TO_PAGES(42)在所有平臺上都等于1,而BYTES_TO_PAGES(5000)在4KB頁的平臺上為2,在8KB頁的平臺上為1。
?BYTE_OFFSET 返回虛擬地址的字節偏移部分。例如,在4KB頁的計算機上,BYTE_OFFSET(0x12345678)的結果為0x678。
?PAGE_ALIGN 把虛擬地址舍向上一個頁邊界。例如,在4KB頁的計算機上,PAGE_ALIGN(0x12345678)的結果為0x12345000。
?ADDRESS_AND_SIZE_TO_SPAN_PAGES 返回從指定虛擬地址開始的指定字節數所跨過的頁數。例如,在4KB的計算機上, ADDRESS_AND_SIZE_TO_SPAN_PAGES(0x12345FFF,2)的結果為2,因為這兩個字節跨過了頁邊界。
3)Windows NT把內核模式地址空間分成分頁內存池和非分頁內存池。(用戶模式地址空間總是分頁的) 必須駐留的代碼和數據放在非分頁池;不必常駐的代碼和數據放在分頁池中。
執行在高于或等于DISPATCH_LEVEL級的代碼不可以引發頁故障。
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGE")
#endif
data_seg編譯指示使所有在其后聲明的靜態數據變量進入分頁池。這個編譯指示與alloc_text完全不同。一個分頁段可以從#pragma data_seg("PAGE")出現的地方開始到#pragma data_seg()出現的地方結束。而Alloc_text僅應用于單個函數。
??? 為了檢測編譯器是否是Microsoft的編譯器,可以測試預定義宏_MSC_VER是否存在。
掉電期間是釋放鎖定內存頁的最佳時期。
| 服務函數 | 描述 |
| MmLockPagableCodeSection | 鎖定含有給定地址的代碼段 |
| MmLockPagableDataSection | 鎖定含有給定地址的數據段 |
| MmLockPagableSectionByHandle | 用MmLockPagableCodeSection返回的句柄鎖定代碼段(僅用于Windows 2000) |
| MmPageEntireDriver | 解鎖所有屬于某驅動程序的頁 |
| MmResetDriverPaging | 恢復整個驅動程序的編譯時分頁屬性 |
| MmUnlockPagableImageSection | 為一個鎖定代碼段或數據段解鎖 |
| 服務函數或宏 | 描述 |
| PushEntryList | 向鏈表頂加入元素 |
| PopEntryList | 刪除最上面的元素 |
4)把C/C++編程規范引入驅動編程中
如RemoveHeadList是一個宏,如果if語句中不用{},則出問題。
5)圖示顯示了 lookaside鏈表的概念。假設有一個可以在水池中直上直下平衡的玻璃杯子。這個杯子就代表lookaside鏈表對象。當初始化該對象時,你告訴系 統需要多大的內存塊(杯中的水滴)。在早期版本的Windows NT中,你還要指出杯子的容量,但現在的操作系統可以自動適應。為了滿足一個內存分配請求,系統首先嘗試著從鏈表中取出(刪除)一塊內存(從杯子中取出一 滴水)。如果連一塊內存也沒有,系統就從外面內存池中取。相反,釋放內存時,系統首先嘗試著放到鏈表上(向杯子中加入一滴水)。但是,如果鏈表滿了,那么 這個內存塊就返回到外界的內存池中(杯子中的水溢出到水池)。
圖示. 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串處理函數
| 操作 | ANSI串函數 | Unicode串函數 |
| 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數據的服務函數
表示. 處理blob數據的服務函數
| 服務函數或宏 | 描述 |
| memchr | 在blob中尋找一個字節 |
| memcpy, RtlCopyBytes, RtlCopyMemory | 復制字節,不允許重疊 |
| memmove, RtlMoveMemory | 復制字節,允許重疊 |
| memset, RtlFillBytes, RtlFillMemory | 用給定的值填充blob |
| memcmp, RtlCompareMemory, RtlEqualMemory | 比較兩個blob |
| memset, RtlZeroBytes, RtlZeroMemory | blob清零 |
?內存的“copy”和“move”操作之間的區別在于可否容忍源和目的相重疊。move操作不管源和目的是否重疊。而copy操作在源和目的有任何重疊時不工作。
?“byte” 操作和“memory”操作的區別是操作的間隔尺寸。byte操作保證按字節為單位執行。而memory操作可以在內部使用更大的塊,所有這些塊的和等于 指定的字節數。這個區別會根據平臺的不同而改變,在32位Intel計算機上,byte操作實際上是對應memory操作的宏。但在Alpha平臺 上,RtlCopyBytes與RtlCopyMemory是完全不同的函數。
5、注冊表
| 服務函數 | 描述 |
| IoOpenDeviceRegistryKey | 打開PDO專用鍵 |
| IoOpenDeviceInterfaceRegistryKey | 打開與注冊設備接口相連的鍵 |
| RtlDeleteRegistryValue | 刪除一個注冊表值 |
| RtlQueryRegistryValues | 從注冊表中讀取多個值 |
| RtlWriteRegistryValue | 向注冊表寫一個值 |
| ZwClose | 關閉注冊表鍵句柄 |
| ZwCreateKey | 創建一個注冊表鍵 |
| ZwDeleteKey | 刪除一個注冊表鍵 |
| ZwEnumerateKey | 枚舉子鍵 |
| ZwEnumerateValueKey | 枚舉某注冊表鍵中的值 |
| ZwFlushKey | 把注冊表更改提交到磁盤 |
| ZwOpenKey | 打開一個注冊表鍵 |
| ZwQueryKey | 取關于某注冊表鍵的信息 |
| ZwQueryValueKey | 取某個注冊表鍵中的值 |
| ZwSetValueKey | 置某個注冊表鍵中的值 |
表示. 注冊表訪問函數
6、浮點數
??? 在Intel處理器上,浮點協處理器還可以執行MMX指令。在歷史上,驅動程序在執行浮點運算上有兩個問題。對于沒有浮點協處理器的計算機,操作系統將用 軟件仿真一個,但是仿真的浮點協處理器會消耗很大的CPU處理能力,并且需要一個處理器異常來捕捉浮點指令。在內核模式中處理異常,尤其是在提升的 IRQL級上,是困難的。另外,在有浮點協處理器的計算機上,由于CPU結構上的原因,當線程上下文切換時,需要一個耗時的操作來保存和恢復浮點協處理器 的狀態。所以,通常的做法是禁止在內核模式驅動程序中使用浮點運算。
??? Microsoft建議,除非必要,應避免在內核模式驅動程序中使用浮點運算。
關于如何使用及更多信息,可以參見《Windows驅動程序模型設計》,P99
轉載于:https://www.cnblogs.com/mydomain/archive/2011/01/04/1925441.html
總結
以上是生活随笔為你收集整理的47、Windows驱动程序模型笔记(五),内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SGI STL 学习笔记二 vector
- 下一篇: 《Orange’s 一个操作系统的实现》