驅動程式記憶體分配

2021-05-17 13:51:14 字數 3997 閱讀 2887

一、 何謂可分頁和非分頁記憶體

預設情況下,核心載入器會載入所有的**部分和全域性資料到非分頁記憶體中。而且,載入器是一次載入整個驅動的可執行檔案,包括相關的 dll 。載入後,核心載入器關閉驅動程式檔案,甚至你可以刪除當前正在執行的驅動檔案。

但是,你可以告訴載入器你希望驅動的哪部分是可分頁,所謂可分頁,就是可能會被換頁出記憶體( page out )。可以使用下面的指令來實現:

#define alloc_pragma

#pragma alloc_text(page, function_name1)

#pragma alloc_text(page, function_name2)

#endif

由 function_namex 指定的函式**將被放置於可分頁記憶體中。

使資料段可分頁,使用下面的編譯指令:

#ifdef alloc_pragma

#pragma data_seg(page)

// define your pageeble data section module here.

#pragma data_seg()

要注意,絕不能讓可能在高的 irql 級別被呼叫的例程被換出頁面。

可以呼叫 mmlockpageablecodesection 和 mmlockpageablecodesection-

byhandle 來鎖定被標誌為可分頁的**段。

可以呼叫 mmlockpageabledatasection 和 mmlockpageabledatasectionb-

yhandle 來鎖定被標誌為可分頁的資料段

可以呼叫 mmunlockpageableimagesection 來解除被上面列出的函式鎖定的**

或資料段。

可以呼叫 mmpageentiredriver 使整個驅動程式可分頁,覆蓋使用編譯指令修飾的段的頁面屬性。

可以呼叫 mmresetdriverpaging 把頁面屬性重設回最初描述的屬性。

最後,把那些驅動初始化後不再需要的**自動丟棄可以使用這些編譯指令:

#ifdef alloc_pragma

#pragma alloc_text(init, driverentry)

#pragma alloc_text(init, function_name) // function called by driverentry

#endif

驅動程式在執行時可能需要動態分配記憶體空間,這時你要決定需要的是可分頁還是不可分頁的記憶體。如果你的驅動在執行中訪問記憶體的時候能夠經受頁錯誤,那麼盡量使用可分頁記憶體。

注意:大多數低層磁碟和網路驅動通常不能使用可分頁記憶體,因為他們的**常常在較高的 irql 等級執行而不允許頁錯誤。但是,檔案系統(通常比磁碟驅動占用更大,更多資源)有時候可從可分頁池中分配一些記憶體。

非分頁內存在整個系統中是乙個有限的資源,其數量依賴於系統使用的型別,和系統可用的物理記憶體。 nt 提供下面的例程給核心驅動來分配記憶體:

exallocatepool

exallocatepoolwithquota

exallocatepoolwithtag

exallocatepoolwithquotatag

呼叫這些函式來請求記憶體時,必須要指定請求的記憶體的型別:

nonpagedpool    請求分配乙個不可分頁的記憶體

pagedpool         請求分配乙個可分頁的記憶體

如果你在分配的記憶體裡有任何同步結構的話,決不要分配分頁記憶體。

當你的應用訪問記憶體時候可以處理頁錯誤的時候,應該指定這個型別。

nonpagedpoolmustsucceed

在其它方式都失敗時,而你又必須立即得到記憶體的時候可以使用這個標誌型別。注意這種型別的記憶體是極度缺乏的資源,可能不足 16k 。注意,只有在其它途徑都失敗的時候才使用,如果分配失敗,將會導致系統的 bugcheck ,錯誤**是 must_succeed_pool_empty 。

nonpagedpoolcachealigned

這個標誌分配使用資料快取線的尺寸來在 cpu 特定的邊界對齊的非分頁記憶體。注意這個操作預設是在 intel 平台上的 nonpagedpool 分配型別。

pagedpoolcachealigned

這個標誌分配使用資料快取線的尺寸來在 cpu 特定的邊界對齊的分頁記憶體。

nonpagedpoolcachealignedmustsucceed

參考 nonpagedpoolmustsucceed 和 nonpagedpoolcachealigned

記憶體池分配器初始化了一些列表,每個列表包含一種固定大小的塊。當你使用上面的函式請求記憶體時,例程試圖分配乙個和你請求數量相近的或更大一點的固定大小的塊。但是,如果你要求的數量超過一頁時,或者超過列表中最大塊的大小時,又或者在預先分配的列表中沒有可用的塊的時候, vmm 就會從任何適當型別的系統可用的記憶體中分配你請求的數量記憶體給你。

當預先分配的列表空了的時候, vmm 會分配至少一頁的記憶體,切分,然後把剩下的資料放進適當的塊列表中。但是,當你請求的非分頁記憶體的數量超過 page_size 時候,記憶體池分配例程不會切分未使用的部分,這會浪費寶貴的非分頁記憶體。

也可以使用 mmallocatenoncachedmemory 或 mmallocatecontiguousmemory

來分配非分頁或物理連續記憶體。它們通常不使用在檔案系統或者過濾驅動中,而是用於執行池例程或者其它結構。

核心驅動如果重複的分配和釋放小塊的記憶體(小於乙個 page_size ) , 可能導致系統的可用物理記憶體碎片化。這會給系統帶來各種問題,包括降低系統的效能等。有乙個方法可以避免系統碎片化,就是預先分配一塊合理大小的記憶體,然後自已管理,在這個預先分配的塊中分配和釋放小塊的記憶體,但這種方法有可能會浪費核心記憶體。

二、用池來管理記憶體

上面提到用預先分配一塊合理大小的記憶體來自已管理,可以避免系統記憶體碎片。我們可以用池來管理這塊預先分配的記憶體。必須再次強調,預先分配的記憶體大小必須足夠準確,太大會浪費寶貴的資源。

呼叫 exallocatepool 來分配池使用的記憶體,你要選擇從分頁或者非分頁的池中分配,注意你的記憶體片基址必須在 8 位元組的邊界對齊。

還要分配和初始化乙個自旋鎖或者使用其它的同步機制來保護對記憶體塊列表的修改。注意不要在比 dispatch_level 更高的 irql 等級使用池操作例程,因為在更高的 irql 等級不能使用同步結構。

然後定義乙個 zone_header 結構的全域性變數,用來作為這個池的控制結構,並呼叫 exinitializezone 來初始化池頭部。然後,就可以通過呼叫 exallocatefromzone 和

exinterlockedallocatefromzone 來分配自已管理的記憶體塊。這兩個函式的差別在於後者使用了自旋鎖用於操作同步。呼叫 exfreetozone 和 exinterlockedfreetozone 來釋放分配的記憶體。

雖然池幫助減少系統記憶體的碎片,但池還是有一些不足:

1、 驅動程式必須預先為池分配記憶體,這些記憶體可能會閒置很久造成記憶體浪費

2、 你對需要的記憶體的數量必須相當的精確,在很多時候這個很難做到。

3、 當記憶體需求增大時,可以擴大池的尺寸,但是卻不能減小池的尺寸,直到重啟系統

lookaside lists

lookaside lists 是 nt4.0 裡新的特性,它突破了池的限制。

當你呼叫 exinitializenpagedlookasidelist 和 exinitializepagedlookasidelist 初始化 lookaside lists 時不用預先分配記憶體,相反,只有當你有真正需要記憶體的時候才分配。

在初始化時,你必須指定列表的深度,表示尺寸的最大值。相關的函式有 exallocatefromn-

pagedlookasidelist 和 exallocatefrompagedlookasidelist 。我們用乙個 npaged_

lookaside_list 或 paged_lookaside_list 結構變數來儲存 lookaside lists 的狀態,注意這結構一定要從非分頁記憶體中分配。

linux 驅動程式 高階字元驅動程式

ioctl方法 驅動程式的原型實現 int ioctl struct inode inode,struct file filp,unsigned int cmd,unsigned long arg ioctl 命令選擇 位段結構 number direction ioc read ioc write...

《Linux裝置驅動程式》第八章 分配記憶體讀書筆記

gfp atomic gfp user gfp highuser gfp noio,gfp nofs gfp dma gfp highhem gfp cold gfp nowarn gfp high gfp repeat,gfp nofail,gfp noretry 記憶體區段 size引數 sla...

linux裝置驅動程式 字元裝置驅動程式

先留個 有一起學習驅動程式的加qq295699450 字元裝置驅動 這篇比較惱火。載入成功,但是讀不出來資料,有知道怎麼回事的,留個言,一起討論下 資料結構 struct scull mem struct scull dev dev 整個驅動程式 如下 include include include...