現代系統核心概論

Post on 05-Jan-2016

51 views 1 download

description

現代系統核心概論. --------------------------------------------------------. Windows 記憶體管理. 第七組 陳威伸 995202006 吳欣倫 995202101 張鈞為 995202069 李佳珉 995202022 陳弘展 995202019 張育銓 995202026 黃龍旺 995202092. Windows 記憶體管理. --------------------------------------------------------. 分頁錯誤處理. - PowerPoint PPT Presentation

Transcript of 現代系統核心概論

1

現代系統核心概論

第七組陳威伸 995202006吳欣倫 995202101張鈞為 995202069李佳珉 995202022陳弘展 995202019張育銓 995202026黃龍旺 995202092

Windows 記憶體管理--------------------------------------------------------

2

Windows 記憶體管理

黃龍旺 995202092

分頁錯誤處理--------------------------------------------------------

3

4.4.3 分頁錯誤處理

• 當處理器再轉譯一個位址時,一旦看到無效的 PDE 或PTE ,它就會觸發一個分頁錯誤,並且將控制權交給設定的相關陷阱處理器 (_KiTrap0E) 。

• _KiTrap0E 先把控制權交給記憶體管理員的 MmAccessFault函式處理分頁錯誤。

( 有效 - 無效 V位元 )

行程分頁目錄

PDE 0

1

分頁表

P2 的 PTE

P1 的 PTE

實體記憶體

頁面 P1

4

4.4.3 分頁錯誤處理• 發生分頁錯誤的情形

– 轉譯虛擬位址時,如果 PDE 或 PTE 的 V 位元為 0– 無效 PTE 又分為以下一些情形

頁面位於分頁檔或對應檔案中 存取一個尚在記憶體中但正在轉移過程中的頁面 存取一個尚未提交的頁面 存取一個要求零的頁面

– 在使用者模式下存取只能在核心模式下才允許存取的頁面– 寫唯讀或防護的頁面– 執行的程式碼位於“不可執行”的頁面中– 寫一個標記為”寫時複製”的頁面分頁錯誤分成兩類:位址轉譯不成功與 PTE 中的存取控制位元檢查沒通過 .

5

4.4.3 分頁錯誤處理• MmAccessFault 需要判斷所有這些情形,並且在可

能的情況下解決分頁錯誤,然後傳回處理的結果– 分頁錯誤解決成功– 存取違例– 防護頁面違例– 頁面換入失敗

1. 處理層級超過 APC_LEVEL 之上的分頁錯誤, Windows核心的設計,執行此層級之上的程式碼不允許分頁。

2. 處理系統位址空間3. 處理使用者位址空間

6

4.4.3 分頁錯誤處理• 系統位址空間– 檢查 PDE

• 無效-M iCheckPdeForPagedPool• 由於不同行程位址空間的分頁目錄是獨立的,所以,分頁記憶

體區的 PDE 有可能不一致, Windows 使用這種在分頁錯誤處理常式中更新 PDE 的方法來保持分頁記憶體區分頁表對應的一致性。

– 檢查 PTE ( 有效 )• MiCheckSystemPteProtection 檢查虛擬位址是否落在系統全域

設定的 PTE 對應保護區,若是,則由該函式直接處理此錯誤。• 檢查位址存取是否合法,不合法的寫入操作,則直接出錯

KeBugCheckEx• 執行的權限是否正確,最後設置 PTE 的髒位元 ( 第 6 位元 ).

7

4.4.3 分頁錯誤處理• 系統位址空間• 檢查 PTE ( 無效 )

– 原型為 1確認存取的虛擬位址不是在非分頁記憶體中,不是的話用MiPteToProto 巨集得到原型 PTE 的指標

– 原型為 0 轉移為 0 保護為 0 則直接出錯 (KeBugCheckEx)– 轉移為 1 正在處理轉移的頁面,若寫權限不足則出錯。

8

4.4.3 分頁錯誤處理• 使用者位址空間– 檢查 PDE

如果 PDE 無效,則對於零 PDE 的情形,看是否為防護分頁,否則將它變成一個要求零的 PDE ,呼叫 MiDispatchFault 解決 PDE 無效的問題,若回傳以後 PDE 扔然無效,則回傳錯誤,否則設置 PDE的髒位。

– 檢查 PTE( 有效 )• 檢查 “寫時複製”頁面

MiCopyOnWrite 處理多個行程同時共享相同的代碼,當其中一個行程要進行寫入則會影響其他行程的正常運作,為每個有寫入的行程複製一個新的頁面。

9

4.4.3 分頁錯誤處理• 使用者位址空間– 檢查 PTE( 有效 )• 檢查 “要求零頁面”

MiResolveDemandZeroFault ,會根據需要申請一個零頁面,以滿足目前的分頁錯誤。

• 檢查是否有 “零 PTE”

MicheckVirtualAddress ,檢查發生錯誤的虛擬位址的 VAD可以找到,用 VAD 的資訊建構一個 PTE否,則將它解析為一個 “要求零頁面”的 PTE ,並未它分配一個頁面,然後回傳。

10

4.4.3 分頁錯誤處理• 使用者位址空間– 檢查 PTE( 無效 )

• 正在轉移的無效 PTEMiResolveTransitionFault ,把正在轉移的頁面從它所在的串列中移除,並重新設置 PTE ,使它變成一個有效的 PTE 。

• 要求一個零頁面的無效 PTEMiResolveDemandZeroFault ,向系統要一個記憶體頁面 (透過MiRemoveZeropage 或 MiremoveAnyPage) ,然後設置好 PFN資料庫中對應該頁面的項目,以及出錯虛擬位址的硬體 PTE 。

11

4.4.3 分頁錯誤處理• 使用者位址空間– 檢查 PTE( 無效 )

• 分頁檔錯誤MiResolvePageFileFault ,雖然依次分頁錯誤只發生在一個頁面上,盡可能地聚集多個相鄰的頁面,以便將它們一起從分頁檔中讀近來,從而提高應用程式的執行效率,然後將出錯頁面的 PTE 設置為正在轉移 .

12

現代系統核心概論

263-268995202026 張育銓

13

4.4.4 Windows 的寫時複製• 寫時複製 (copy-on-write) 是現代作業系統的

一個重要特性

• 父行程、子行程有獨立的位址空間、獨立的分頁表,但分頁表指向相同的實體頁面

• 所有的資料頁面定義成”唯獨”

14

4.4.4 Windows 的寫時複製• 若要其修改 ( 頁面中的任一位元組 ) ,系統

就會捕捉到存取違例,再其複製,讓兩個行程有自己私有的頁面,皆為可讀寫

• 將複製的動作延遲到真正需要兩個行程分配各自私有頁面的時候,避免不必要的資料複製,減緩了對記憶體的需求

15

4.4.4 Windows 的寫時複製• 記憶體區段物件建立 NtCreateSection和

MmCreateSection 中的參數 SectionPageProtection指定了記憶體區段物件中頁面的保護屬性

- PAGE_READ ( 讀 ) - PAGE_READWRITE (完全共用,只有一個 ) - PAGE_EXECUTE ( 只允許執行代碼,讀寫都不行 ) - PAGE_WRITECOPY (私有複製, copy on write)

16

4.4.4 Windows 的寫時複製• MmCreateSection -MiCreateImageFileMap 透過此函式建立的映像檔記憶體區預設情形皆

為寫時複製的,除非 SECTION 有特別設定不相容的保護屬性

• MmCreateSection -MiCreateDataFileMap 頁面保護屬性為 MM_EXECUTE_READWRITE ,

可讀寫、可執行不可寫時複製

17

4.4.4 Windows 的寫時複製• MmCreateSection -MiCreatePagingFileMap 保護屬性是由 MmCreateSection 的參數

SectionPageProtection透過MiMakeProtectionMask 函式變化而來, PAGE_WRITECOPY 變為MM_WRITECOPY

18

4.4.4 Windows 的寫時複製• Windows 為硬體 PTE 定義的資料結構

MMPTE_HARDWARE 中,第 9 位元是一個保留位元, Windows 將他解釋成CopyOnWrite 位元

19

4.4.4 Windows 的寫時複製• 實作過程 1. 考慮一個寫時複製的頁面是如何被第一次分配的

- 第一次存取頁面時,硬體 PTE 是無效的 -MiCompleteProtoPteFault ,呼叫巨集來

填充 PTE 的位元。

20

4.4.4 Windows 的寫時複製• MiCompleteProtoPteFault - 定義中,寫時複製保護屬性被解釋成

0x200 ,即 PTE 中的第 9 位元被設定,而寫為元 ( 第 1 位元 ) 沒有被設定。發生分頁錯誤

21

4.4.4 Windows 的寫時複製• 2. 由於一個行程對一個支援寫時複製的頁

面執行了寫入操作,觸發一個分頁錯誤 - MmAccessFault 檢測

→ 呼叫 MiCopyOnWrite

22

4.4.4 Windows 的寫時複製• MiCopyOnWrite 1. 根據參數中指定的出錯位址找到 PFN資料庫中對應的項,印證他是一個原型 PTE

2. 透過 MiRemoveAnyPage 申請一個實體頁面

3. 呼叫 MiInitializeCopyOnWritePfn初始化其PFN 對應的項

4. 從系統 PTE 區域中申請一個空閒 PTE ,完成記憶體頁面的複製

23

4.4.4 Windows 的寫時複製• MiCopyOnWrite 5. 填好出錯位址的 PTE項目 6. 將舊頁面在 PFN資料庫中的計數減一

24

4.5 實體記憶體管理• PFN (Page Frame Number)資料庫 - 管理系統的實體記憶體

25

4.5.1 PFN資料庫Typedef struct _MMPFN{

• union { • PFN_NUMBER Flink • ULONG WsIndex • PKEVENT Event • NTSTATUS ReadStatus • SINGLE_LIST_ENTRY NextStackPfn • } u1 • PMMPTE PteAddress • union { • PFN_NUMBER Blink • ULONG_PTR ShareCount • } u2 • union { • struct { • USHORT ReferenceCount • MMPFNENTRY e1 • }

26

4.5.1 PFN資料庫• struct { • USHORT ReferenceCount • USHORT ShortFlags • } e2 • } u3 • union { • MMPTE OriginalPte • LONG AweReferenceCount • }; • union { • ULONG_PTR EntireFrame • struct { • ULONG_PTR PteFrame:25 • ULONG_PTR InPageError:1 • ULONG_PTR VerifierAllocation:1 • ULONG_PTR AweAllocation:1 • ULONG_PTR Priority:3 • ULONG_PTR MustBeCached:1 • } • } u4

27

4.5.1 PFN資料庫• Extern, *PMMPFN• #define MI_PFN_ELEMENT(index) (&MmPfnDatabase[index])

( 以分頁框架編號為索引 )

一個有效的 PTE 可以快速的找到其頁面的PFN資料庫

28

4.5.1 PFN資料庫• 每個行程和系統都有一個工作集 ( 頁面可

能屬於,可能不屬於 )

• 工作集共八種狀態

29

4.5.1 PFN資料庫• 活動狀態 (active) - 也稱有效狀態 (vaild) ,正在被某個行程使用,

或是被用於系統空間。

• 備用狀態 (standby) - 本來屬於某個行程或是系統工作集,但是現

在已經從工作集中移除。 - PTE仍然指向,但標記無效 - 系統回收,或是工作集回收

30

4.5.1 PFN資料庫• 已修改狀態 (modified) - 已從原來的工作集中移除 - PTE仍然指向,但標記無效 - 如系統要回收,必須將內容寫到磁碟上

• 已修改但不寫出 (modified no-write) - 記憶體管理員不會將內容寫到磁碟上

31

4.5.1 PFN資料庫• 轉移狀態 - 說明一個頁面正在進行 I/O動作 - 兩個緒程並行的在同一個頁面上引發分頁錯

誤時,可以正確處理

• 空閒狀態 - 不屬於任何工作集 - 重新使用其頁面以前,為了安全考慮清除髒資料

32

4.5.1 PFN資料庫• 零化狀態 - 頁面是空閒的,且不屬於任何工作集 - 內容全部歸零

• 壞狀態 - 頁面產生硬體錯誤,系統不再使用此頁面 - 唯一不為記憶體管理員管理和調度

現代系統核心概論

275-279

4.5.3 實體頁面串列的管理和操作• Color 的參數 : 就在 MMPFN 的資料結構

中, u3.e1 會有一個 4 位元的 PageColor 的成員欄位,代表一個 pfn項目的顏色

• MmStandbyPageListByPriority– 而非 MmStandbyPageListHead

• 因為 windows 實作的備用頁面有優先順序, MMPFN 的資料結構會有 3 位元 Priority 的欄位 (預設是 3)

4.5.3 實體頁面串列的管理和操作• Color 在串列初始化時,有兩個全域變數

• MmSecondaryColors 64– 此系統定義 64 種顏色

• MmSecondaryColorMask 63– (111111 最低六位元 )

4.5.3 實體頁面串列的管理和操作• MmFreePagesByColor[0][0] - [0][63]– 代表顏色 0 到 63 的零化頁面之串列開頭

• MmFreePagesByColor[1][0] - [1][63]– 代表顏色 0 到 63 的空閒頁面之串列開頭

• 以上的串列為 Double link list

4.5.3 空閒和零化頁面的插入和刪除1. 空閒頁面的插入 (STEP 1)

• 空閒頁面的插入 (MiInsertPageInFreeList)(程式碼鏈結 )• 00500 /* Get the free page list and increment its count */• 00501 ListHead = &MmFreePageListHead;• 00502 ASSERT_LIST_INVARIANT(ListHead);• 00503 ListHead->Total++;• 00504 • 00505 /* Get the last page on the list */• 00506 LastPage = ListHead->Blink;• 00507 if (LastPage != LIST_HEAD)• 00508 {• 00509 /* Link us with the previous page, so we're at the end now */• 00510 MI_PFN_ELEMENT(LastPage)->u1.Flink = PageFrameIndex;• 00511 }• 00512 else• 00513 {• 00514 /* The list is empty, so we are the first page */• 00515 ListHead->Flink = PageFrameIndex;• 00516 }

首先將頁面插入到串列尾部 !

4.5.3 空閒和零化頁面的插入和刪除1. 空閒頁面的插入 (STEP 2)

• 空閒頁面的插入 (MiInsertPageInFreeList) ( 程式碼鏈結 )• 00549 /* Get the page color */• 00550 Color = PageFrameIndex & MmSecondaryColorMask;//利用 MASK

取出最低六位元的 Color 。• 00551 • 00552 /* Get the first page on the color list */• 00553 ColorTable = &MmFreePagesByColor[FreePageList][Color];//找出對

應的 ColorTable 。 FreePageList:1( 代表空閒頁面 )

• 根據找到的資訊把頁面插入到顏色串列(MmFreePagesByColor[FreePageList][Color]) 的尾部,回傳

4.5.3 空閒和零化頁面的插入和刪除2. 零化頁面的插入

• 空閒頁面的插入 (MiInsertPageInList) ( 程式碼鏈結 )

• /* Only used for zero pages in ReactOS */• 00622 ListName = ListHead->ListName• 00623 ASSERT(ListName == ZeroedPageList);• 00624 ListHead->Total++;

• 將頁面插入到 ZeroedPageList 的前端

• /* Get the page color */• 00673 Color = PageFrameIndex & MmSecondaryColorMask;• 00674 • 00675 /* Get the list for this color */• 00676 ColorHead = &MmFreePagesByColor[ZeroedPageList][Color];• ZeroedPageList:0( 代表零化頁面 )

• 找尋對應顏色的 ColorHead ,將頁面插入到 MmFreePagesByColor[ZeroedPageList][Color]中

4.5.3 空閒和零化頁面的插入和刪除3. 在串列開頭移除一個零化或空閒頁面 (STEP1)

• 在串列開頭移除一個零化或空閒頁面• (STEP1)-MiRemovePageInList()• 從指定的串列開頭移除一個頁面,然後更新顏色串列。• (STEP2)- MiRemovePageByColor() ( 程式碼鏈結 )• /* Could be either on free or zero list */• 00246 ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];• 00247 ASSERT_LIST_INVARIANT(ListHead);• 00248 ListName = ListHead->ListName;• 00249 ASSERT(ListName <= FreePageList);• 00250 • 00251 /* Remove a page */• 00252 ListHead->Total--;

• /* Get the first page on the color list */• 00292 ASSERT(Color < MmSecondaryColors);• 00293 ColorTable = &MmFreePagesByColor[ListName][Color];• 00294 ASSERT(ColorTable->Count >= 1);• 從指定的”顏色和串列”,從開頭移除一個零化或空閒頁面,然後更新相關顏色串列

• MiUnlinkFreeOrZeroedPage() (程式碼鏈結 )• 00096 /* Find the list for this entry, make sure it's the free or zero list */• 00097 ListHead = MmPageLocationList[Entry->u3.e1.PageLocation];

• 根據頁面的u3.e1.PageLocation成員找到串列,並利用他去找出前後的LINK,將中間的頁面刪除

• 00098 ListName = ListHead->ListName;• 00099 ASSERT(ListHead != NULL);• 00100 ASSERT(ListName <= FreePageList);• 00101 ASSERT_LIST_INVARIANT(ListHead);• • 00107 /* Get the forward and back pointers */• 00108 OldFlink = Entry->u1.Flink;//前後的LINK• 00109 OldBlink = Entry->u2.Blink;• • 00135 /* Get the page color */• 00136 OldBlink = MiGetPfnEntryIndex(Entry);• 00137 Color = OldBlink & MmSecondaryColorMask;• 00138 • 00139 /* Get the first page on the color list */• 00140 ColorTable = &MmFreePagesByColor[ListName][Color];

• 依樣也從自己的顏色串列,從雙串列中去除

4.5.3 空閒和零化頁面的插入和刪除4. 在串列中間移除一個零化或空閒頁面 (STEP1)

4.5.3 有 Priority 的備用串列• MMPFN 的資料結構會有 3 位元的欄位 Priority– 因此有八個備用串列分別存放各個優先順序頁面

• 之前說過備用串列 :MmStandbyPageListHead , 其實沒使用。

• 而是利用有 Priority 的備用串列 : MmStandbyPageListByPriority

4.5.3備用頁面的插入和刪除1. 在串列前端插入備用頁面

• 根據頁面 PFN項目的 u4.Priority找串列開頭,將頁面插入串列中

• 而參數MiInsertPageInList() 中的指定了 MmStandbyPageListHead 作為串列開頭– 例如 :00501 ListHead = &MmFreePageListHead;– 但是真正的目標串列開頭也是會轉換成

MmStandbyPageListByPriority陣列去對應優先順序的串列,其餘插入串列的方法沒變化

4.5.3備用頁面的插入和刪除2. 在串列中移除一個備用頁面

• 縮寫– MSPLB_P : MmStandbyPageListByPriority– MSPL_H : MmStandbyPageListHead

• 在 MiRemovePageFromList() 要移除備用頁面也是不能指定MSPL_H 作為備用串列,而必須是 MSPLB_P陣列的一員。其餘就是雙串列中移除一個節點

• 注意 :備用頁面的 PTE 為轉移狀態,需透過MiRestoneTransitionPte() 使他不為轉移狀態。

• 在 MiUnlinkPageFromList() 如果根據 PFN項目得到的串列為MSPL_H ,則一樣轉換為 MSPLB_P

4.5.3修改串列• 修改串列兩部分 :

• 1.OriginalPte.u.Soft.Prototype 為 0( 非原型 PTE)– 頁面放於 MmModifiedPageListByColor[0]串列中– 頁面的外部記憶體是分頁檔

• 2. 其餘– 頁面放於 MmModifiedPageListHead 全域串列中 ( 其中也包含了位於分頁

檔的那些頁面 )– 頁面的外部記憶體是對應檔案

• 修改後的頁面透過 MiInsertPageInList()插入• 修改後的頁面透過 MiUnlinkPageFromList() 移除

4.5.3修改但不寫出頁面串列( 為單獨處理的串列 )

• 是將頁面修改,但記憶體管理員的修改頁面寫出器 ( 下一節介紹 ) 不會將頁面寫到外部記憶體

• 在串列尾部插入頁面 :MiInsertPageInList()

• 在串列前端插入頁面 :MiInsertFrontModifiedNoWrite()

• 刪除 : MiUnlinkPageFromList()

• 因為記憶體管理員不會自動調度該串列的頁面,因此如果要被刷新到外部記憶體,必須要利用MmEnableModifiedWriteOfSection() 移除該串列的頁面

4.5.3修改但不寫出頁面串列• MmEnableModifiedWriteOfSection() 作法 :

• 1. 利用 MiUnlinkPageFromList()從串列移除

• 2. 利用 MiInsertPageInList()插入到修改串列

• 3. 該頁面變成普通修改頁面,因此修改頁面寫出器就可以將頁面內容寫到外部記憶體了。

4.5.3修改但不寫出頁面串列• 用途 :NTFS 檔案系統利用這種頁面來對應系統中的中繼資料

(Metadata)

• 因為 Metadata 不會自動存到磁碟中,除非當記錄 Metadata修改情況的資訊被寫到磁碟中,這些 Metadata 才被允許被寫入磁碟。

• Why?– 因為利用以上特性,可以保證Metadata 的任何修改都可以被保存,一

但資料消失, Metadata 就可以恢復

• 缺點 :– 這些頁面會佔據實體記憶體,需要到空間問題。

4.5.3壞頁面串列• 1. 不參與系統的頁面調度

• 2.利用 MiInsertPageInList()插入到壞頁面串列

• 3. 不論任何頁面的 PFN項目欄位u3.e1.RemoveRequested被設定,則一定會被放入壞頁面串列

• 4. 理論上,壞頁面放入後就不在移除

4.5.3 兩個實體頁面申請函式• 先找出可用的頁面,才可申請,因此兩個實體頁面申請

函式,只是搜尋可用的頁面,移除後回傳

• 第一個:MiRemoveZeroPage(Color) ( 程式碼鏈結 )

• 1. 根據 Color 找出零化串列該顏色的頁面,利用MiRemovePageByColor()去移除該顏色的串列開頭的頁面,並回傳。

• 2. 根據 Color 找不到零化顏色串列該顏色的頁面,則移除其他顏色的頁面,並回傳。

4.5.3 兩個實體頁面申請函式• 3. 步驟 1.2.都找不到,用同樣邏輯檢查空閒串列,但是找到的頁面需經過頁面零化MiZeroPhysicalPage() ,再回傳

• 4. 空閒串列又為空,就檢查備用串列 (依序從 0 到最高 ) ,並呼叫 MiRemoveZeroPage()移除一個頁面,一樣經過頁面零化,再回傳

4.5.3 兩個實體頁面申請函式• 與MiRemoveZeroPage(Color) (程式碼鏈結)相仿,只是搜尋串列的順序不同,且都不需要零化• 第二個 :MiRemoveAnyPage(Color) (程式碼鏈結) • 00360 PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;//該顏色的空閒串列• 00361 if (PageIndex == LIST_HEAD)• 00362 {• 00363 /* Check the colored zero list */• 00364 PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink; //該顏色的零化串列• 00365 if (PageIndex == LIST_HEAD)• 00366 {• 00367 /* Check the free list */• 00368 ASSERT_LIST_INVARIANT(&MmFreePageListHead);• 00369 PageIndex = MmFreePageListHead.Flink; ;//整個空閒串列• 00370 Color = PageIndex & MmSecondaryColorMask;• 00371 if (PageIndex == LIST_HEAD)• 00372 {• 00373 /* Check the zero list */• 00374 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);• 00375 PageIndex = MmZeroedPageListHead.Flink; ;//整個零化串列• 00376 Color = PageIndex & MmSecondaryColorMask;

• ….都找不到就檢查備用串列 (依序從0到最高 )

4.5.3 MDL(Memory Descriptor List)

• MDL描述的記憶體為一組實體頁面

• 當核心或裝置驅動程式需要多個實體記憶體頁面時, Windows 提供了一個稱為 MDL 的資料結構來描述這些頁面

• 而 PFN緊跟在 MDL物件之後,可透過 Pages=(PPFN_NUMBER)(Mdl+1); 去存取到包含這些頁面的 PFN陣列

4.5.3 MDL(Memory Descriptor List)

• Windows 核心的應用程式使用 virtual address ,去存取記憶體,但有些為了資料傳輸效能或某些硬體特性 (DMA) ,必須要在 physical address工作,此時 MDL 為一個便捷的途徑。

• MmAllocatePageForMdl(): 申請實體記憶體,回傳一個 MDL物件

• MmAllocatePageForMdlEx(): 申請實體記憶體,回傳一個MDL物件

• MmFreePageFromMdl():釋放MDL物件中的實體記憶體

4.5.3 MDL(Memory Descriptor List)

• 為了在指定的位址範圍內獲得足夠的記憶體, MiAllocatePageForMdl() 會搜尋零化、空閒、備用串列,去滿足需求。但是可能實際申請到的記憶體頁面數會少於需求,因此我們需要檢查 MDL物件的值。

• MiAllocatePageForMdl() 不會將實體記憶體對應到 virtual address ,如果需要 virtual address 需要自己轉換。

現代系統核心概論

286-296

4.6 工作集管理

995202022 李佳珉

工作集管理• 何謂工作集 ( Working set)

– 工作集是用來描述一個 Process 使用的實體頁面集的總合– 每個 process 都有專門的頁面存放 working set list ,而此頁面在 Process 的位址

空間建立時就已經對應好

• 工作集管理– 計算工作集最小最大值並根據使用需求加以管控

• 三種概念– 行程工作集– 系統工作集– 工作階段工作集

• 此 4.6節介紹 Windows 對行程工作集的管理

4.6.1 WINDOWS 工作集管理員

工作集資訊 (1)

• 從哪取得 process 的工作集串列 ?– 位址 : 位於全域變數 MmWorkingSetList 中– MmWorkingSetList 型別為 MMWSL ,定義如下一頁

MMWSL 型別定義typedef struct _MMWSL {

WSLE_NUMBER FirstFree; //指向第一個空閒串列的開頭,添加工作集頁面的位址WSLE_NUMBER FirstDynamic; //指向第一個可以被修剪的頁面WSLE_NUMBER LastEntry; //指向最後一個使用的項WSLE_NUMBER NextSlot; // The next slot to trimPMMWSLE Wsle; WSLE_NUMBER LastInitializedWsle;WSLE_NUMBER NonDirectCount;PMMWSLE_HASH HashTable;ULONG HashTableSize;ULONG NumberOfCommittedPageTables;PVOID HashTableStart;PVOID HighestPermittedHashAddress;ULONG NumberOfImageWaiters;ULONG VadBitMapHint;

USHORT UsedPageTableEntries[MM_USER_PAGE_TABLE_PAGES];ULONG CommittedPageTables[MM_USER_PAGE_TABLE_PAGES/(sizeof(ULONG)*8)];

} MMWSL, *PMMWSL;

MMWSL 型別定義typedef struct _MMWSL {

WSLE_NUMBER FirstFree; //第一個空閒項WSLE_NUMBER FirstDynamic; //指向第一個可以被修剪的頁面WSLE_NUMBER LastEntry; // 最後一項WSLE_NUMBER NextSlot; // The next slot to trim

PMMWSLE Wsle; WSLE_NUMBER LastInitializedWsle;WSLE_NUMBER NonDirectCount;PMMWSLE_HASH HashTable;散列表ULONG HashTableSize;ULONG NumberOfCommittedPageTables;PVOID HashTableStart;PVOID HighestPermittedHashAddress;ULONG NumberOfImageWaiters;ULONG VadBitMapHint;

USHORT UsedPageTableEntries[MM_USER_PAGE_TABLE_PAGES];ULONG CommittedPageTables[MM_USER_PAGE_TABLE_PAGES/(sizeof(ULONG)*8)];

} MMWSL, *PMMWSL;

Wsle 是一個指標,實際上指向一個陣列,每個陣列描述一個有效的 Process 記憶體空間的頁面______________________________________Wsle 的 member 型別是 MMWSLE型別定義如下頁

MMWSLE 型別定義typedef struct _MMWSLENTRY {ULONG_PTR Valid : 1;ULONG_PTR LockedInWs : 1;ULONG_PTR LockedInMemory : 1;ULONG_PTR Protection : 5;ULONG_PTR Hashed : 1;ULONG_PTR Direct : 1;ULONG_PTR Age : 2;ULONG_PTR VirtualPageNumber : 20; } MMWSLENTRY;

typedef struct _MMWSLE {union {PVOID VirtualAddress;ULONG_PTR Long;MMWSLENTRY e1;} u1;

} MMWSLE;

每個 MMWSLE 描述了一個有效頁面,從定義可看出,低 12 位元是 MMWSLENTRY 中定義的旗標位元,高 20 位元是頁面的虛擬位址

工作集資訊 (2)

• 工作集的最小值、最大值如何定義 ??– 最小值 : process 剛建立即設定

MmCreateProcessAddressSpace( IN ULONG MinimumWorkingSetSize, IN PEPROCESS NewProcess, OUT PULONG_PTR DirectoryTableBase);

工作集資訊 (2)

• 工作集的最小值、最大值如何定義 ??– 最小值 : process 剛建立即設定

MmCreateProcessAddressSpace( IN ULONG MinimumWorkingSetSize, IN PEPROCESS NewProcess, OUT PULONG_PTR DirectoryTableBase);

工作集最小值

工作集資訊 (2)

• 工作集的最小值、最大值如何定義 ??– 最小值 : process 剛建立即設定

– 在 4.3.1節中, WRK 中預設最小值是 50 ,最大值是 345– 最大值最小值是可以突破的

• 當 Process 使用很多頁面,且系統確實有足夠的可用頁面時, Process 的工作集可以超越原本設定的最大值,但總數量還是不能超過系統設定的最大值 (也就是變數 MmMaximumWorkingSetSize ,此變數在系統初始化就設定好 )

• 當系統記憶體吃緊時,且 Process 不需用到很多頁面時,工作集數量可能降低到最小值以下

MmCreateProcessAddressSpace( IN ULONG MinimumWorkingSetSize, IN PEPROCESS NewProcess, OUT PULONG_PTR DirectoryTableBase);

修剪工作集• 背景– 在前面章節中,工作集管理員會定期檢查各個 Process

的工作集,為了有效的使用實體記憶體,必要時須修剪占用記憶體較多的 Process 工作集

• 如何取得修剪 Process 所需的資訊 ?– 就在 Process 的 EPROCESS 資料結構 中 VM 成員所指向型別為 MMSUPPORT 裡面

– VM 成員所指的地方包含了工作管理員在判斷是否需要修剪一個 Process 所需的各種資訊,其中也包含了 Process 設定的工作集最小值最大值

MMSUPPORT 型別定義typedef struct _MMSUPPORT {LIST_ENTRY WorkingSetExpansionLinks; //允许修剪的Process將會加入一個串列MmWorkingSetExpansionHead

LARGE_INTEGER LastTrimTime;

MMSUPPORT_FLAGS Flags;ULONG PageFaultCount;WSLE_NUMBER PeakWorkingSetSize;WSLE_NUMBER GrowthSinceLastEstimate;

WSLE_NUMBER MinimumWorkingSetSize; //工作集最小值WSLE_NUMBER MaximumWorkingSetSize; //工作集最大值struct _MMWSL *VmWorkingSetList;WSLE_NUMBER Claim; //修剪優先级

WSLE_NUMBER NextEstimationSlot;WSLE_NUMBER NextAgingSlot;WSLE_NUMBER EstimatedAvailable;WSLE_NUMBER WorkingSetSize;

EX_PUSH_LOCK WorkingSetMutex;

} MMSUPPORT, *PMMSUPPORT;

MMSUPPORT 型別定義typedef struct _MMSUPPORT {LIST_ENTRY WorkingSetExpansionLinks; //允许修剪的Process將會加入一個串列MmWorkingSetExpansionHead

LARGE_INTEGER LastTrimTime;

MMSUPPORT_FLAGS Flags;ULONG PageFaultCount;WSLE_NUMBER PeakWorkingSetSize;WSLE_NUMBER GrowthSinceLastEstimate;

WSLE_NUMBER MinimumWorkingSetSize;WSLE_NUMBER MaximumWorkingSetSize;struct _MMWSL *VmWorkingSetList;WSLE_NUMBER Claim; //修剪優先级

WSLE_NUMBER NextEstimationSlot;WSLE_NUMBER NextAgingSlot;WSLE_NUMBER EstimatedAvailable;WSLE_NUMBER WorkingSetSize;

EX_PUSH_LOCK WorkingSetMutex;

} MMSUPPORT, *PMMSUPPORT;

如果 Process 允許被修剪,他的 EPROCESS 的 Vm.WorkingSetExpansionLinks 成員會被加入到一個以全域變數 MmWorkingSetExpansionHead 為串列開頭的串列中,

也就是以 EPROCESS 的 Vm.WorkingSetExpansionLinks 為節點構成一個串列,且串列開頭為 MmWorkingSetExpansionHead

修剪工作集工作集管理員的主要函式

是 MmWorkingSetManager

MmWorkingSetManager 每隔 1秒觸發 當可用的記憶體降低至某大小觸發

觸發時機

工作集管理員根據準則修剪 Process 工作集-----------------------------------------------------------------------------------------------------

-MmWorkingSetManager 呼叫 MiComputerSystemTrimCriteria 函式

以確定是否需要啟動修剪工作集或刷新工作集年齡

修剪工作集工作集管理員的主要函式是

MmWorkingSetManager

MmWorkingSetManager 每隔 1秒觸發 當可用的記憶體降低至某大小觸發

觸發時機

工作集管理員根據準則修剪 Process 工作集-----------------------------------------------------------------------------------------------------

-MmWorkingSetManager 呼叫 MiComputerSystemTrimCriteria 函

式以確定是否需要啟動修剪工作集或刷新工作集年齡

修剪工作集• WorkingSetManager() 中首先呼叫

MiComputeSystemTrimCriteria() 函數確定是否需要進行以下操作,主要包括修剪工作集、刷新工作集年齡以及空操作,這三種情況的觸發條件如下 :– i. 修剪觸發條件

• 由以上可知,根據以下三條件– 目前系統可用頁面數 Available少於當前需要的頁面數– 目前工作集中,已經有被替換頁面的記錄, MiReplacing == TRUE– 有超過系統可用頁面數四分之一的頁面被重心循環利用當作備用頁面

• 以上條件滿足一個就可立即進行修剪操作,並設定 Criteria 標準的變數

參考連結

修剪工作集• WorkingSetManager() 中首先呼叫

MiComputeSystemTrimCriteria() 函數確定是否需要進行以下操作,主要包括修剪工作集、刷新工作集年齡以及空操作,這三種情況的觸發條件如下 :– i. 修剪觸發條件

• 由以上可知,根據以下三條件– 目前可用頁面數 Available少於當前需要的頁面數– 目前工作集中,已經有被替換頁面的記錄, MiReplacing == TRUE– 有超過可用頁面數四分之一的頁面被重心循環利用當作備用頁面

• 以上條件滿足一個就可立即進行修剪操作,並設定 Criteria 標準的變數

修剪操作 :1. 呼叫 MiRearrangeWorkingSetExpansionList 以安排如何有

效的修剪工作集2. 傳回 MI_TRIM_ALL_WORKING_SETS

• WorkingSetManager() 中首先調用 MiComputeSystemTrimCriteria() 函數確定當前需要進行的操作,主要包括工作集修剪、工作集老化以及空操作,這三種情況的觸發條件如下 :– i.修剪觸發條件

• 由以上可知,根據以下三條件– 目前可用頁面數 Available少於當前需要的頁面數– 目前工作集中,已經有被替換頁面的記錄, MiReplacing == TRUE– 有超過可用頁面數四分之一的頁面被重心循環利用當作備用頁面

• 以上條件滿足一個就可立即進行修剪操作,並設定 Criteria 標準的變數

修剪操作 :1. 呼叫 MiRearrangeWorkingSetExpansionList 以安排如何

有效的修剪工作集2. 傳回 MI_TRIM_ALL_WORKING_SETS

• MiRearrangeWorkingSetExpansionList 函式– 用途 :• 把允許修剪得工作集 (也就是前面提到的

MmWorkingSetExpansionHead 串列中的工作集 ) ,分成不同類別,並按照修剪優先順序重新排序。

– 原則 :• EPROCESS 的 Vm->Claim 越大,放在修剪串列的前部, Process 閒置時間越長放在串列前部。• 上述 Vm->Claim 是工作管理員對行程工作集的評估值,此值統計了老化情況,也就是系統記憶體吃緊時, Process 可為系統提供多少頁面的記憶體。

– ii. 刷新年齡 觸發條件

• 由以上可知, 當不滿足修剪條件時且目前可用頁面小於有限值 MM_ENORMOUS_LIMIT 時,進行老化操作 OutFlags = MI_AGE_ALL_WORKING_SETS 為 return 值

– iii. 不操作• 當上兩種都不滿足時,代表目前系統還有大量可用的記憶體,

OutFlags = 0; 作為 return 值,不做任何操作

修剪、刷新工作集• 真正實施修剪工作或刷新工作集年齡的函式 – MiProcessWorkingSets• 工作 :

– 針對 MmWorkingSetExpansionHead 串列中的每一個工作集,附加到該 Process 中,並根據參數的指示進行下面的處理

• 處理分兩部分 :– 1. 修剪– 2. 刷新工作集年齡

修剪工作集• 1. 修剪 :

– 若要求修剪所有工作集,• 呼叫 MiDetermineTrimAmount

– 確定是否需要修剪此 Process 工作集– 修剪多老的頁面– 是否須重新計算頁面年齡– 傳回修剪頁面數量

– 若確定要對 Process 修剪• MiProcessWorkingSets 呼叫 MiTrimWorkingSet 函式

– 執行修剪工作– 指定參數 : 期望從工作集中修剪多少頁面、工作集資訊,以及多少的頁面可被修剪

– 若修剪完成• MiProcessWorkingSets 呼叫 MiAgeWorkingSet 函式

– 更新工作集中頁面的年齡值– 估計此 Process 的 Vm->Claim 值

修剪工作集• 2. 更新工作集年齡 :– 若要求更新工作集中的年齡值,

• 呼叫 MiAgeWorkingSet 函式 – 更新頁面年齡值– 估計此 Process 的 Vm->Claim 值

工作集頁面的年齡計算 延伸

• 對於工作集的每一個頁面,其 MMWSLE 結構中的 u1.e1.Age 欄位記錄了這個頁面的年齡。 由於此欄位只有兩 bits ,所以只能表達 0、 1、 2、 3 ,計算方法如下 :

– 1. 判斷頁面 PTE 的”存取過”位元是否已設定,如果被設定了,代表這個頁面從上次檢查以後已經被存取過,於是年齡值為 0 ,並清除該位元

– 2. 如果”存取過”位元被設定,則代表上次檢查以來此頁面未被存取過,於是年齡值加 1 ,最大不超過 3

MMWSLE 型別定義typedef struct _MMWSLENTRY {ULONG_PTR Valid : 1;ULONG_PTR LockedInWs : 1;ULONG_PTR LockedInMemory : 1;ULONG_PTR Protection : 5;ULONG_PTR Hashed : 1;ULONG_PTR Direct : 1;

ULONG_PTR Age : 2;ULONG_PTR VirtualPageNumber : 20; } MMWSLENTRY;

typedef struct _MMWSLE {union {PVOID VirtualAddress;ULONG_PTR Long;MMWSLENTRY e1;} u1;

} MMWSLE;

工作集頁面的年齡計算 延伸

• 對於工作集的每一個頁面,其 MMWSLE 結構中的 u1.e1.Age 欄位記錄了這個頁面的年齡。 由於此欄位只有兩 bits ,所以只能表達 0、 1、 2、 3 ,計算方法如下 :

– 1. 判斷頁面 PTE 的”存取過”位元是否已設定,如果被設定了,代表這個頁面從上次檢查以後已經被存取過,於是年齡值為 0 ,並清除該位元

– 2. 如果”存取過”位元沒被設定,則代表上次檢查以來此頁面未被存取過,於是年齡值加 1 ,最大不超過 3

釋放頁面過程• 由 MiTrimWorkingSet 函式實作

– 流程 :• 從 Process 的工作集串列找出一批符合條件的頁面

– 條件 : 年齡值大於等於參數設定的修剪年齡• 呼叫 MiFreeWsleList 函式,由此函式將滿足條件的頁面變成轉移狀態,轉

移至備用串列或修改串列• 呼叫 MiRemoveWlse 函式將這些頁面移除( 上面一直循環,直到指定數量的頁面被修剪掉,或掃描完整個工作集陣列的動態區域。 )

• 補充 :– MiTrimWorkingSet 函式只是把被移除了頁面在陣列中做了無效的標

記,所以如果 MiTrimWorkingSet 函式完成修剪工作後,發現工作集串列中存在許多無效的頁面,就會呼叫 MiRemoveWorkingSetPages 壓縮工作集串列,清除無效的資料

工作集串列的組織

工作集串列的組織

工作集串列 MMWSL 物件內部結構

工作集串列的組織MMWSL資料結構中,FirstDynamic 成員指向第一個可被修改的頁面故在 MiTrimWorkingSet 函式中可看到,工作集修剪都從 FirstDynamic 開始

工作集串列 MMWSL 物件內部結構

工作集串列的組織• 如何根據一個頁面的虛擬位址或 PTE 找到

其在工作集串列中的項 ??

FirstFree 成員指向一個空閒串列開頭,這些項在內部形成一個單串列,

LastEntry 成員指向最後一個使用項,因此 MiTrimWorkingSet 在修剪工作集時絕對不會

超過 LastEntry 項

工作集串列 MMWSL 物件內部結構

工作集串列與雜湊表的組織結構• 如何根據一個頁面的虛擬位址找到在工作

集串列中的項 ?–利用 MiLocateWsle 函式

MiLocateWsle 函式• 方法– 先根據參數中提示的索引項目,判斷是否此索引項目指向此虛擬位址• 若是,則成功找到工作集串列• 若否,則利用參數中指定的虛擬位址在雜湊清單中搜尋– 若找到,則傳回雜湊項目中的 Index– 若找不到,則在工作串列中進行線性搜尋, 直至找到為止,若還是找不到,則回傳失敗

工作集串列與雜湊表的組織結構• 如何加速 WLSE 搜尋過程 ?– 使用雜湊表, HashTable 成員

工作集串列 MMWSL 物件內部結構

Hash table 成員• 管理方式

– 由於 Wlse 和 HashTable 位於 Process 位址空間的固定位置處,空間管理方式直接以頁面對應做管理,而不是透過記憶體集區管理

– Hashtable 所佔據的頁面也算在工作集內

• 以下是雜湊表成員項 MMWSLE_HASH 定義Typedef struct _MMWSLE_HASH{

PVOID Key;WSLE_NUMBER Index;

} MMWSLE_HASH, *PMMWSLE_HASH;

Hash table 成員• 管理方式

– 由於 Wlse 和 HashTable 位於 Process 位址空間的固定位置處,空間管理方式直接以頁面對應做管理,而不是透過記憶體集區管理

– Hashtable 所佔據的頁面也算在工作集內

• 以下是雜湊表成員項 MMWSLE_HASH 定義Typedef struct _MMWSLE_HASH{

PVOID Key;WSLE_NUMBER Index;

} MMWSLE_HASH, *PMMWSLE_HASH;

• 每一項都記載從虛擬位址到工作集串列陣列索引的關聯

• 雜湊表本身使用簡單的運算作為虛擬位置的對應• 使用線性開放定址法 ( Open Addressing with Linear

Probing ) 找到空閒的雜湊桶 ( bucket hashing ) ,插入雜湊表中

Hash table 成員• 管理方式

– 由於 Wlse 和 HashTable 位於 Process 位址空間的固定位置處,空間管理方式直接以頁面對應做管理,而不是透過記憶體集區管理

– Hashtable 所佔據的頁面也算在工作集內

• 以下是雜湊表成員項 MMWSLE_HASH 定義Typedef struct _MMWSLE_HASH{

PVOID Key;WSLE_NUMBER Index;

} MMWSLE_HASH, *PMMWSLE_HASH;

• 每一項指定了一個從頁面虛擬位址到工作集串列陣列索引的關聯,工作集串列中的雜湊表使用簡單的運算作為虛擬位置的對應

• 使用 線性開放定址法 ( Open Addressing with Linear Probing ) 找到空閒的雜湊桶 ( bucket hashing )

線性開放定址法

線性開放定址法

4.6.2 平衡集管理員

995202019 陳弘展

平衡集管理員• 平衡集– 所有具備資源分配資格的行程的集合

• 平衡集管理員– 是一個系統緒程,系統初始化時建立– 優先順序 16 ,最低的即時優先順序– 目的:維持系統記憶體資源的平衡

平衡集管理員• 概念– 當系統記憶體吃緊時

1.從擁有較多記憶體的行程的工作集中換出一些頁面2. 把不滿足執行條件的行程排除在平衡集之外

• 實作– 基本上是工作集管理員的一個容器,為它提供一個執

行環境 ( 每 1秒觸發一次 )– 每 4 或 8秒啟動行程 /堆疊交換器 (process/stack

swapper)

平衡集管理員• 程式碼 base\ntos\ke\balmgr.c• 主函式 KeBalanceSetManager (line 142-326)

主要工作:1. 呼叫函式 MmWorkingSetManager2.向 行程 /堆疊交換器 發送事件 KiSwapEvent3. 呼叫 ExAdjustLookasideDepth & KiAdjustTrpCredits ( 不探討 )

4.7 記憶體監視工具 MEMMON

MemMon

• MemMon 是一個記憶體監視工具,能夠即時顯示目前系統中記憶體使用的情況,包含:– 系統位址空間的配置– 各個實體記憶體串列的頁面數量– 各行程的虛擬位址空間配置– 各行程的工作集資訊

MemMon 使用介紹 (1/2)

▲ 顯示系統記憶體資訊的 MemMon 介面

目前系統中實體記憶體&

分頁檔中頁面使用量的百分比

目前系統中實體記憶體總量、可用量和系統快取

目前系統中總共可提交的記憶體數量 和 還有多少可提交分頁集區 和 非分頁集區的記憶體總量 及其總和

4.5.3節提到的記憶體串列

系統位址空間 和 記憶體配置結構

MemMon 使用介紹 (2/2)

▲ 顯示行程記憶體資訊的 MemMon 介面

目前系統中的行程樹

所選行程的分頁錯誤次數、虛擬記憶體大小、提交頁面數

所選行程的工作集大小、最小值、最大值和尖峰值

所選行程的位址空間配置結構

MemMon 實作原理 (1/2)系統記憶體介面部份:

1. Windows API - GetPerformanceInfo (at psapi.dll)- GlobalMemoryStatusEx (at kernel32.dll)→ 護得 實體記憶體、分頁檔、核心記憶體資訊、控制碼數、

行 程數、緒程數2.裝置驅動程式

透過驅動,利用核心模組的符號資訊,直接讀取核心變數的值 ( 實體記憶體串列變數和系統位址空間各區域的位置資訊 )

MemMon 實作原理 (2/2)行程記憶體介面部份:

1. Windows API - Module32First , Module32Next (at kernel32.dll)→ 護得單一個行程的模組資訊- Heap32First , Heap32Next (at kernel32.dll)→ 護得有關堆積的資訊- Thread32First , Thread32Next (at kernel32.dll)→ 護得有關緒程的資訊※要先呼叫 CreateToolhelp32Snapshot 函式

2.裝置驅動程式透過驅動,獲得 EPROCESS物件中的有關資訊 ( 虛擬記憶體、

工作 集資訊 ) 以及存取緒程中的 KTHREAD 來獲得堆疊資訊

4.8 本章總結

到這裏我們學到了…• 如何在系統空間中有效管理可分頁、不可分頁的記憶體 ?• 如何為行程建立獨享的位址空間,並實作共用記憶體 ?• 系統如何解決分頁錯誤 ?• 如何管理實體記憶體 ?• 如何管理行程的工作集 ?

沒提到的:- Windows 記憶體管理員內建了各種最佳化設施 (e.g. Logical Prefetcher)

- 對於傳統 Unix 系統的核心支援 (e.g. fork)

相關課題:-快取管理 (Chapter 7)- 檔案 I/O (Chapter 6)

Thanks for your attention.