程式碼鏈結

Download Report

Transcript 程式碼鏈結

現代系統核心概論
--------------------------------------------------------
Windows 記憶體管理
第七組
陳威伸
995202006
吳欣倫
995202101
張鈞為
995202069
李佳珉
995202022
陳弘展
995202019
張育銓
995202026
黃龍旺
995202092
1
Windows 記憶體管理
--------------------------------------------------------
分頁錯誤處理
黃龍旺 995202092
2
4.4.3分頁錯誤處理
(有效-無效 V位元)
行程分頁目錄
實體記憶體
分頁表
頁面P1
PDE
P2的PTE
0
P1的PTE
1
• 當處理器再轉譯一個位址時,一旦看到無效的PDE或PTE,
它就會觸發一個分頁錯誤,並且將控制權交給設定的相關
陷阱處理器(_KiTrap0E)。
• _KiTrap0E先把控制權交給記憶體管理員的MmAccessFault
函式處理分頁錯誤。
3
4.4.3分頁錯誤處理
• 發生分頁錯誤的情形
– 轉譯虛擬位址時,如果PDE或PTE的V位元為0
– 無效PTE又分為以下一些情形
 頁面位於分頁檔或對應檔案中
 存取一個尚在記憶體中但正在轉移過程中的頁面
 存取一個尚未提交的頁面
 存取一個要求零的頁面
–
–
–
–
在使用者模式下存取只能在核心模式下才允許存取的頁面
寫唯讀或防護的頁面
執行的程式碼位於“不可執行”的頁面中
寫一個標記為”寫時複製”的頁面
分頁錯誤分成兩類:
位址轉譯不成功與PTE中的存取控制位元檢查沒通過.
4
4.4.3分頁錯誤處理
• MmAccessFault需要判斷所有這些情形,並且在可
能的情況下解決分頁錯誤,然後傳回處理的結果
–
–
–
–
分頁錯誤解決成功
存取違例
防護頁面違例
頁面換入失敗
1. 處理層級超過APC_LEVEL之上的分頁錯誤,Windows
核心的設計,執行此層級之上的程式碼不允許分頁。
2. 處理系統位址空間
3. 處理使用者位址空間
5
4.4.3分頁錯誤處理
• 系統位址空間
– 檢查PDE
• 無效-MiCheckPdeForPagedPool
• 由於不同行程位址空間的分頁目錄是獨立的,所以,分頁記憶
體區的PDE有可能不一致,Windows使用這種在分頁錯誤處理
常式中更新PDE的方法來保持分頁記憶體區分頁表對應的一致
性。
– 檢查PTE (有效)
• MiCheckSystemPteProtection檢查虛擬位址是否落在系統全域設
定的PTE對應保護區,若是,則由該函式直接處理此錯誤。
• 檢查位址存取是否合法,不合法的寫入操作,則直接出錯
KeBugCheckEx
• 執行的權限是否正確,最後設置PTE的髒位元(第6位元).
6
4.4.3分頁錯誤處理
• 系統位址空間
• 檢查PTE (無效)
– 原型為 1
確認存取的虛擬位址不是在非分頁記憶體中,不是的話用
MiPteToProto巨集得到原型PTE的指標
– 原型為 0 轉移為 0 保護為 0
則直接出錯(KeBugCheckEx)
– 轉移為 1
正在處理轉移的頁面,若寫權限不足則出錯。
7
4.4.3分頁錯誤處理
• 使用者位址空間
– 檢查PDE
如果PDE無效,則對於零PDE的情形,看是否為防護分頁,否則將
它變成一個要求零的PDE,呼叫MiDispatchFault解決PDE無效的問
題,若回傳以後PDE扔然無效,則回傳錯誤,否則設置PDE的髒位。
– 檢查PTE(有效)
• 檢查 “寫時複製”頁面
MiCopyOnWrite處理多個行程同時共享相同的代碼,當其中一個
行程要進行寫入則會影響其他行程的正常運作,為每個有寫入
的行程複製一個新的頁面。
8
4.4.3分頁錯誤處理
• 使用者位址空間
– 檢查PTE(有效)
• 檢查 “要求零頁面”
MiResolveDemandZeroFault,會根據需要申請一個零頁面,以滿
足目前的分頁錯誤。
• 檢查是否有 “零PTE”
MicheckVirtualAddress,檢查發生錯誤的虛擬位址的VAD
可以找到,用VAD的資訊建構一個PTE
否,則將它解析為一個 “要求零頁面”的PTE,並未它分配一
個頁面,然後回傳。
9
4.4.3分頁錯誤處理
• 使用者位址空間
– 檢查PTE(無效)
• 正在轉移的無效PTE
MiResolveTransitionFault,把正在轉移的頁面從它所在的串列中
移除,並重新設置PTE,使它變成一個有效的PTE。
• 要求一個零頁面的無效PTE
MiResolveDemandZeroFault,向系統要一個記憶體頁面(透過
MiRemoveZeropage 或 MiremoveAnyPage),然後設置好PFN資
料庫中對應該頁面的項目,以及出錯虛擬位址的硬體PTE。
10
4.4.3分頁錯誤處理
• 使用者位址空間
– 檢查PTE(無效)
• 分頁檔錯誤
MiResolvePageFileFault,雖然依次分頁錯誤只發生在一個頁面
上,盡可能地聚集多個相鄰的頁面,以便將它們一起從分頁檔
中讀近來,從而提高應用程式的執行效率,然後將出錯頁面的
PTE設置為正在轉移.
11
現代系統核心概論
263-268
995202026 張育銓
12
4.4.4 Windows的寫時複製
• 寫時複製(copy-on-write)是現代作業系統的
一個重要特性
• 父行程、子行程有獨立的位址空間、獨立
的分頁表,但分頁表指向相同的實體頁面
• 所有的資料頁面定義成”唯獨”
13
4.4.4 Windows的寫時複製
• 若要其修改(頁面中的任一位元組),系統就
會捕捉到存取違例,再其複製,讓兩個行
程有自己私有的頁面,皆為可讀寫
• 將複製的動作延遲到真正需要兩個行程分
配各自私有頁面的時候,避免不必要的資
料複製,減緩了對記憶體的需求
14
4.4.4 Windows的寫時複製
• 記憶體區段物件建立NtCreateSection和
MmCreateSection中的參數SectionPageProtection指
定了記憶體區段物件中頁面的保護屬性
- PAGE_READ (讀)
- PAGE_READWRITE (完全共用,只有一個)
- PAGE_EXECUTE (只允許執行代碼,讀寫都不行)
- PAGE_WRITECOPY (私有複製,copy on write)
15
4.4.4 Windows的寫時複製
• MmCreateSection
-MiCreateImageFileMap
透過此函式建立的映像檔記憶體區預設情形皆
為寫時複製的,除非SECTION有特別設定不相
容的保護屬性
• MmCreateSection
-MiCreateDataFileMap
頁面保護屬性為MM_EXECUTE_READWRITE,可
讀寫、可執行不可寫時複製
16
4.4.4 Windows的寫時複製
• MmCreateSection
-MiCreatePagingFileMap
保護屬性是由MmCreateSection的參數
SectionPageProtection透過
MiMakeProtectionMask函式變化而來,
PAGE_WRITECOPY變為MM_WRITECOPY
17
4.4.4 Windows的寫時複製
• Windows為硬體PTE定義的資料結構
MMPTE_HARDWARE中,第9位元是一個保
留位元,Windows將他解釋成CopyOnWrite
位元
18
4.4.4 Windows的寫時複製
• 實作過程
1. 考慮一個寫時複製的頁面是如何被第一
次分配的
-第一次存取頁面時,硬體PTE是無效的
-MiCompleteProtoPteFault,呼叫巨集來
填充PTE的位元。
19
4.4.4 Windows的寫時複製
• MiCompleteProtoPteFault
- 定義中,寫時複製保護屬性被解釋成
0x200,即PTE中的第9位元被設定,而寫為
元(第1位元)沒有被設定。發生分頁錯誤
20
4.4.4 Windows的寫時複製
• 2. 由於一個行程對一個支援寫時複製的頁
面執行了寫入操作,觸發一個分頁錯誤
- MmAccessFault檢測
→ 呼叫MiCopyOnWrite
21
4.4.4 Windows的寫時複製
• MiCopyOnWrite
1. 根據參數中指定的出錯位址找到PFN資
料庫中對應的項,印證他是一個原型PTE
2. 透過MiRemoveAnyPage申請一個實體頁
面
3. 呼叫MiInitializeCopyOnWritePfn初始化其
PFN對應的項
4. 從系統PTE區域中申請一個空閒PTE,完
成記憶體頁面的複製
22
4.4.4 Windows的寫時複製
• MiCopyOnWrite
5. 填好出錯位址的PTE項目
6. 將舊頁面在PFN資料庫中的計數減一
23
4.5 實體記憶體管理
• PFN (Page Frame Number)資料庫
- 管理系統的實體記憶體
24
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
}
25
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
26
4.5.1 PFN資料庫
• Extern, *PMMPFN
• #define MI_PFN_ELEMENT(index)
(&MmPfnDatabase[index])
(以分頁框架編號為索引)
一個有效的PTE可以快速的找到其頁面的PFN
資料庫
27
4.5.1 PFN資料庫
• 每個行程和系統都有一個工作集 (頁面可能
屬於,可能不屬於)
• 工作集共八種狀態
28
4.5.1 PFN資料庫
• 活動狀態(active)
- 也稱有效狀態(vaild),正在被某個行程使用,
或是被用於系統空間。
• 備用狀態(standby)
- 本來屬於某個行程或是系統工作集,但是現
在已經從工作集中移除。
- PTE仍然指向,但標記無效
- 系統回收,或是工作集回收
29
4.5.1 PFN資料庫
• 已修改狀態(modified)
- 已從原來的工作集中移除
- PTE仍然指向,但標記無效
- 如系統要回收,必須將內容寫到磁碟上
• 已修改但不寫出(modified no-write)
- 記憶體管理員不會將內容寫到磁碟上
30
4.5.1 PFN資料庫
• 轉移狀態
- 說明一個頁面正在進行I/O動作
- 兩個緒程並行的在同一個頁面上引發分頁錯
誤時,可以正確處理
• 空閒狀態
- 不屬於任何工作集
- 重新使用其頁面以前,為了安全考慮清除髒
資料
31
4.5.1 PFN資料庫
• 零化狀態
- 頁面是空閒的,且不屬於任何工作集
- 內容全部歸零
• 壞狀態
- 頁面產生硬體錯誤,系統不再使用此頁面
- 唯一不為記憶體管理員管理和調度
32
現代系統核心概論
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);
•
從指定的”顏色和串列”,從開頭移除一個零化或空閒頁面,然後更新相關顏色串列
4.5.3空閒和零化頁面的插入和刪除
4.在串列中間移除一個零化或空閒頁面(STEP1)
•
•
•
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
00099
00100
00101
ListName = ListHead->ListName;
ASSERT(ListHead != NULL);
ASSERT(ListName <= FreePageList);
ASSERT_LIST_INVARIANT(ListHead);
00107
00108
00109
/* Get the forward and back pointers */
OldFlink = Entry->u1.Flink;//前後的LINK
OldBlink = Entry->u2.Blink;
00135
00136
00137
00138
00139
00140
/* Get the page color */
OldBlink = MiGetPfnEntryIndex(Entry);
Color = OldBlink & MmSecondaryColorMask;
•
依樣也從自己的顏色串列,從雙串列中去除
/* Get the first page on the color list */
ColorTable = &MmFreePagesByColor[ListName][Color];
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;
PMMWSLE Wsle;
WSLE_NUMBER LastInitializedWsle;
WSLE_NUMBER NonDirectCount;
PMMWSLE_HASH HashTable;
ULONG HashTableSize;
ULONG NumberOfCommittedPageTables;
PVOID HashTableStart;
PVOID HighestPermittedHashAddress;
ULONG NumberOfImageWaiters;
ULONG VadBitMapHint;
//指向第一個空閒串列的開頭,添加工作集頁面的位址
//指向第一個可以被修剪的頁面
//指向最後一個使用的項
// The next slot to trim
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;
//第一個空閒項
//指向第一個可以被修剪的頁面
Wsle//最後一項
是一個指標,
// The next slot to trim
實際上指向一個陣列,
每個陣列描述一個有效的 Process 記憶體空
WSLE_NUMBER LastInitializedWsle;
WSLE_NUMBER NonDirectCount;
間的頁面
PMMWSLE_HASH HashTable;散列表
______________________________________
ULONG HashTableSize;
ULONG NumberOfCommittedPageTables; Wsle 的 member 型別是 MMWSLE
PVOID HashTableStart;
型別定義如下頁
PMMWSLE Wsle;
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;
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 剛建立即設定
MmCreateProcessAddressSpace(
IN ULONG MinimumWorkingSetSize,
IN PEPROCESS NewProcess,
OUT PULONG_PTR DirectoryTableBase
);
– 在4.3.1節中,WRK中預設最小值是50,最大值是345
– 最大值最小值是可以突破的
• 當 Process 使用很多頁面,且系統確實有足夠的可用頁面時,Process 的工作集
可以超越原本設定的最大值,但總數量還是不能超過系統設定的最大值(也就
是變數 MmMaximumWorkingSetSize,此變數在系統初始化就設定好)
• 當系統記憶體吃緊時,且 Process 不需用到很多頁面時,工作集數量可能降低
到最小值以下
修剪工作集
• 背景
– 在前面章節中,工作集管理員會定期檢查各個 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;如果 Process 允許被修剪,
他的 EPROCESS 的 Vm.WorkingSetExpansionLinks 成員會被
WSLE_NUMBER MinimumWorkingSetSize;加入到一個以全域變數 MmWorkingSetExpansionHead 為
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;
//修剪優先级
也就是以 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少於當前需要的頁面數
1. 呼叫 MiRearrangeWorkingSetExpansionList
以安排如何有
– 目前工作集中,已經有被替換頁面的記錄,MiReplacing
== TRUE
效的修剪工作集
– 有超過可用頁面數四分之一的頁面被重心循環利用當作備用頁面
2. 傳回MI_TRIM_ALL_WORKING_SETS
• 以上條件滿足一個就可立即進行修剪操作,並設定 Criteria 標準
的變數
• WorkingSetManager()中首先調用
MiComputeSystemTrimCriteria()函數確定當前需要進
行的操作,主要包括工作集修剪、工作集老化以及
空操作,這三種情況的觸發條件如下:
– i.修剪觸發條件
• 由以上可知,根據以下三條件
修剪操作:
– 目前可用頁面數Available少於當前需要的頁面數
1. 呼叫 MiRearrangeWorkingSetExpansionList
以安排如何有
– 目前工作集中,已經有被替換頁面的記錄,MiReplacing
== TRUE
效的修剪工作集
– 有超過可用頁面數四分之一的頁面被重心循環利用當作備用頁面
2. 傳回MI_TRIM_ALL_WORKING_SETS
• 以上條件滿足一個就可立即進行修剪操作,並設定 Criteria 標準
的變數
• 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 物件內部結構
工作集串列的組織
FirstFree 成員指向一個空閒串列開頭,
這些項在內部形成一個單串列,
• 如何根據一個頁面的虛擬位址或
PTE 找到其
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 位址空間的固定位置處,空
• 使用線性開放定址法
( Open Addressing with Linear
間管理方式直接以頁面對應做管理,而不是透過記憶體集區管理
) 找到空閒的雜湊桶
( bucket hashing ),插入
– Probing
Hashtable
所佔據的頁面也算在工作集內
雜湊表中
• 管理方式
• 以下是雜湊表成員項 MMWSLE_HASH 定義
Typedef struct _MMWSLE_HASH{
PVOID Key;
WSLE_NUMBER Index;
} MMWSLE_HASH, *PMMWSLE_HASH;
Hash table 成員
每一項指定了一個從頁面虛擬位址到工作集串列陣
• •管理方式
列索引的關聯,工作集串列中的雜湊表使用簡單的
– 由於 Wlse 和 HashTable 位於 Process 位址空間的固定位置處,空
運算作為虛擬位置的對應
間管理方式直接以頁面對應做管理,而不是透過記憶體集區管理
• 使用 線性開放定址法 ( Open Addressing with Linear
– Probing
Hashtable
所佔據的頁面也算在工作集內
) 找到空閒的雜湊桶
( bucket hashing )
• 以下是雜湊表成員項 MMWSLE_HASH 定義
Typedef struct _MMWSLE_HASH{
PVOID Key;
WSLE_NUMBER Index;
} MMWSLE_HASH, *PMMWSLE_HASH;
線性開放定址法
線性開放定址法
4.6.2 平衡集管理員
995202019 陳弘展
平衡集管理員
• 平衡集
– 所有具備資源分配資格的行程的集合
• 平衡集管理員
– 是一個系統緒程,系統初始化時建立
– 優先順序 16 ,最低的即時優先順序
– 目的:維持系統記憶體資源的平衡
平衡集管理員
• 概念
– 當系統記憶體吃緊時
1.從擁有較多記憶體的行程的工作集中換出一些頁面
2. 把不滿足執行條件的行程排除在平衡集之外
• 實作
– 基本上是工作集管理員的一個容器,為它提供一個執
行環境(每1秒觸發一次)
– 每4或8秒啟動行程/堆疊交換器 (process/stack swapper)
平衡集管理員
• 程式碼 base\ntos\ke\balmgr.c
• 主函式 KeBalanceSetManager (line 142-326)
主要工作:
1.呼叫函式MmWorkingSetManager
2.向 行程/堆疊交換器 發送事件KiSwapEvent
3.呼叫ExAdjustLookasideDepth & KiAdjustTrpCredits (不探討)
4.7 記憶體監視工具 MEMMON
MemMon
• MemMon是一個記憶體監視工具,能夠即時顯示目前系統
中記憶體使用的情況,包含:
–
–
–
–
系統位址空間的配置
各個實體記憶體串列的頁面數量
各行程的虛擬位址空間配置
各行程的工作集資訊
MemMon使用介紹(1/2)
系統位址空間 和 記憶體配置結構
目前系統中實體記憶體
&
分頁檔中頁面使用量的百分比
目前系統中實體記憶體總量
、可用量和系統快取
4.5.3節提到的記憶體串列
目前系統中總共可提交的記憶
體數量 和 還有多少可提交
分頁集區 和 非分頁集區的記
憶體總量 及其總和
▲ 顯示系統記憶體資訊的MemMon介面
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.