有效使用記憶體的 6 條技巧 來自微軟官網

2021-05-23 13:32:44 字數 3333 閱讀 5090

本頁內容

有效布局資料結構並盡可能重用它們。

在啟動時將未分頁的池記憶體用於長期用途。

經濟有效地使用記憶體

使用後備列表

避免頻繁地建立和取消虛擬位址空間的對映

測試與驗證

本文提供在 microsoft windows 家族作業系統核心模式驅動程式中有效使用記憶體的技巧。有效使用記憶體有助於提高驅動程式效能。以下是有效使用記憶體的 6 條技巧。

設計驅動程式時,根據記憶體型別、大小和生命週期來計畫記憶體分配。合併類似生存期的記憶體分配,這樣就可以在不使用記憶體時盡快將其釋放。不要將大小差距很大的資料結構混合到同乙個記憶體分配中,除非您確定它們能夠恰當地保持一致。

重用資料結構,而不是釋放它們並在以後為其他用途重新分配記憶體。資料結構的重用可以避免額外的重新分配操作,有助於預防記憶體池的碎片化。

處理 i/o 請求時,驅動程式往往需要額外的記憶體。驅動程式可能為特定的 i/o 請求分配記憶體描述符表 (mdl) 或者內部緩衝區,也可能需要分配乙個 irp 併發送到底層驅動程式。這些資料結構的大小根據請求的不同而異。例如,mdl 的大小依賴於它所描述的 mdl 大小。

如果驅動程式擁有一種限制 i/o 大小或分割大的 i/o 請求的技術,那麼您可以固定緩衝區的大小,從而固定 mdl 的大小並使緩衝區可重用。

請記住,所有效能問題都涉及到調優和平衡。作為一條通用規則,您應該優化最常用的操作,而不是不常用的大型請求或很少發生的小型請求。

驅動程式通常將未分頁的池記憶體用於長期的 i/o 緩衝區。因為隨著系統的不斷執行,未分頁的池會碎片化,所以驅動程式應該預先分配長期結構所需的記憶體,並在裝置移除時釋放這些記憶體。例如,總是執行 dma 操作、建立若干事件並且使用後備列表的啟動程式應該在啟動時、在driverentry或者 adddevice 例程中為這些物件分配記憶體,並在在處理裝置移除請求時釋放這些記憶體。

但是,驅動程式不應該預分配非常大的記憶體塊(例如幾兆位元組)並試圖在該記憶體塊中管理自己的分配。

合適的記憶體分配例程包括exallocatepoolwithtagexallocatepoolwithquotatagexallocatepoolwithtagpriorityallocatecommonbuffer(如果驅動程式的裝置使用匯流排主控 dma 或者系統 dma 控制器的自動初始化模式)。

驅動程式應該使用池分配例程的有標記版本,而不應使用陳舊的無標記版本。windbg 和許多測試工具使用標記來跟蹤記憶體分配。對池分配進行標記有助於更輕鬆地找到與記憶體相關的 bug。

未分頁的池記憶體是一種有限的系統資源。驅動程式應該盡可能經濟有效地分配記憶體。一般而言,應避免重複呼叫記憶體分配支援例程來請求小於 page_size 的記憶體分配。如果驅動程式通常同時使用幾個相關的資料結構,那麼考慮將這些資料結構**到單個記憶體分配中。例如,scsi 埠驅動程式將 irp、scsi 請求塊 (srb) 和 mdl 繫結到單個記憶體分配中。

使用 dma 的驅動程式例外。如果執行 dma 的驅動程式需要若干個單頁緩衝區,但是這些緩衝區不需要彼此相鄰,那麼它應該分別為每個緩衝區呼叫allocatecommonbuffer。這種方法保持連續的位址空間並改進記憶體分配將要進行的更改。

另外,請考慮您計畫用於分配請求的記憶體分配例程是否會達到下乙個頁面邊界。

如果驅動程式請求小於 page_size 位元組,那麼exallocatepoolwithtag將根據請求的位元組量分配記憶體。如果驅動程式請求等於或大於 page_size,那麼exallocatepoolwithtag將分配乙個頁對齊 (page-aligned) 的緩衝區,這個緩衝區的大小是 page_size 的整數倍。小於 page_size 的記憶體分配不會跨越頁邊界,不必是頁對齊的;但是它們需要與乙個 8 位元組邊界對齊。

allocatecommonbuffer總是分配至少一頁的記憶體。如果驅動程式請求小於 page_size 位元組的整數倍,那麼驅動程式將無法訪問最後一頁的剩餘位元組。

後備列表提供大小固定、可重用的緩衝區。設計它們用於驅動程式需要動態分配但無法預料分配數量的結構。

後備列表既可以從分頁的記憶體池分配,也可以從未分頁的記憶體池分配。驅動程式定義該列表中條目的布局和內容,系統根據需要維護列表狀態並調整可用條目的數量。

驅動程式呼叫exinitialize[n]pagedlookasidelist建立後備列表,呼叫exallocatefrom[n]pagedlookasidelist分配列表中的條目,呼叫exfreeto[n]pagedlookasidelist釋放列表中的條目。列表頭部必須從未分頁記憶體中分配,即使該列表條目本身處於分頁記憶體之中。

頻繁地建立和取消虛擬位址空間的對映會降低系統整體效能,因為這會導致 translation lookaside buffer (tlb) 頻繁重新整理。tlb 是每個處理器的虛擬位址到實體地址轉換的快取。tlb 中的每個條目都包含乙個頁表條目 (pte)。

系統轉換引用新頁的虛擬位址時,它就會將乙個條目新增到 tlb。填滿 tlb 之後,每次新增乙個新增條目時,系統必須丟棄乙個現有條目。結果,每當呼叫方重新對映或取消對映位址空間時都會更改乙個 pte,系統必須中斷所有 cpu 才能更新包含該 pte 的所有 tlb 條目。

就內部而言,i/o 管理器能夠避免irp->mdladdress中的 mdl 發生這種問題。核心模式元件首次呼叫mmgetsystemaddressformdlsafe時,i/o 管理器將系統位址與相應的實體地址一同儲存在 mdl 中。irp 在完成之後返回到 i/o 管理器時,i/o 管理器取消 mdl 的對映。這樣,i/o 管理器僅需要將單個對映(和單個虛擬位址到實體地址的轉換)用於每個 i/o 請求。

使用 driver verifier (verifier.exe)、gflags (gflags.exe) 和 poolmon (poolmon.exe) 來跟蹤、測試和驗證記憶體分配問題。

驅動程式驗證工具能夠從特殊記憶體池分配記憶體並監視驅動程式對分配的記憶體訪問。它能夠檢測出對分配範圍之外的記憶體或已經釋放的記憶體的訪問企圖。另外,它可以檢測出記憶體洩漏。記憶體洩漏是指已經分配,但不再使用並且未釋放的記憶體塊。驗證工具還收集統計資料,統計從特殊記憶體池請求的記憶體分配量以及分配是否成功。

結合使用 gflags 與 driver verifier。可以使用 gflags 配置 driver verifier 的特殊記憶體池選項,或者指定要在單獨的記憶體分配中使用的特殊記憶體池。

poolmon 收集和顯示記憶體分配的多種資料,這些資料按照在分配階段賦予的池標記進行排序。

來自微軟關於異常處理的17條軍規

1.不要返回錯誤 異常是報告框架中的錯誤的主要手段。這個就不討論了,異常包含的資訊量遠不是幾個錯誤 可以替代的.2.通過引發異常來報告執行故障。如果某一成員無法按預期方式成功執行,則應將這種情況視為乙個執行故障並引發乙個異常。例如函式的引數檢測,引數不符合輸入要求,就應該引發乙個異常.另外還有很多情...

20條讓Google搜尋更有效的技巧

google已經成為乙個不可或缺的搜尋工具,每天被數百萬人使用,並滲透到生活的所有層面。無論是工作或學習 研究 尋找電影以及名人的八卦新聞。本文介紹了20條簡單有趣的技巧,讓你告別以往費時費力的搜尋習慣。從現在起,tips你的 搜 能力吧!或。google通常會查詢搜尋框內輸入的所有字詞都包含的頁面...

20條讓Google搜尋更有效的技巧

對上百萬人而言,google是乙個每天都要用到的 生活各方面都要用上的不可或缺的搜尋 工具。從工作 學校 研究 到查詢電影 名人 新聞 八卦,google是乙個萬事通型的搜尋引擎。除了僅僅輸入乙個片語然後費力地在一頁又一頁的搜尋 結果裡查詢答案,還有一些方法能讓你的搜尋更加有效。或。google通常...