修改串列

advertisement
吳藺剛、陳嘉浩、余修丞
吳彥諄、吳政樺、黃詩蘋
劉乃菀、黃柏軒、姜智文
卓卿安、洪國勛、郭星辰
Department of Computer Science & Information Engineering, National Central University
 4.4 記憶體分頁
 4.6 工作集管理
 4.4.3 分頁錯誤處理
 4.6.1 Windows 工作集管理員
 4.4.4 Windows 的寫時複製
 4.6.2 平衡集管理員
 4.5 實體記憶體管理
 4.7 記憶體監視工具
 4.5.1 PFN資料庫
 4.7.1 記憶體監監視工
 4.5.2 實體頁面的狀態變化
 4.4.4 Windows的寫時複製
 4.5.3 實體頁面串列的管理和操作
 4.5.4 修改頁面寫出器
 4.5.5 行程/堆疊交換器
 4.5.6 低/高記憶體通知
 4.8 本章總結
2012/6/13
陳嘉浩
 回顧
 Intel x86 中的 PDE 和 PTE
 無效 PTE 和原型 PTE
 分頁錯誤處理
 分頁錯誤的類型
 錯誤檢查的流程
 錯誤處理的方法
註解
PDE: Page Directory Entry
PTE: Page Table Entry
5
註解
PFN: Page Frame Number
稍候在 4.5 章將會介紹
轉移位元
原型位元
0
分頁檔轉移
54
10 9
12 11
31
0
PFN*
保護
0
0
31
12 11
10 9
保護
0
54
0
0
1
要求一個零頁面
0
1
0
0
位於分頁檔
轉移位元
原型位元
0
分頁框架編號
(PFN)
31
12 11
1
0
10 9
保護
Cd Wt U/S W
5
4
3
2
0
1
31
0
0
1
0
未知原因錯誤
頁面正在轉移
6
指向原型PTE的無效PTE
7
修改自第六組投影片
行程A分頁目錄
行程A分頁表
實體記憶體
記憶體集區的區段物件
行程B分頁目錄
行程B分頁表
無效
分頁檔
8
行程A分頁目錄
行程A分頁表
實體記憶體
記憶體集區的區段物件
行程B分頁目錄
行程B分頁表
無效
有效
分頁檔
當行程B要存取頁面P2時,它依然會發現自己的PTE是無效的,因此它
假設行程A要存取頁面P2,它會發現分頁表中P2的PTE是無效的,但它
實體頁面分配完成後,行程A分頁表中的PTE以及原型PTE都會指向該
會檢查原型PTE,並且發現P2已經在實體記憶體中了。接著P2分頁表
會指向原型PTE,而原型PTE此時也是無效的且指向分頁檔中的頁面,
實體分頁,此時行程A分頁表中的PTE以及原型PTE都為有效,而行程
中的PTE則會重新指向到實體頁面
此時記憶體管理員會根據原型PTE的指示為P2分配一個實體頁面
B分頁表中的PTE則保持原狀
解決辦法
分配一個實體頁面並且從
硬碟上讀入內容
 無效 PTE
解決辦法
• 頁面位於分頁檔或對應檔案中
回傳值
STATUS_SUCCESS
申請一個填滿零的頁面,
• 存取一個尚在記憶體中但正在轉移過程中的頁面
解決辦法
並加入到目前行程的工作
STATUS_PAGE_FAULT_PAGING_FILE
• 存取一個尚未提交的頁面
集 STATUS_IN_PAGE_ERROR
複製目前行程私有的頁面,
•
存取一個要求零的頁面
並替換原來的頁面
解決辦法
回傳值
 在使用者模式下存取只能在核心的頁面
回傳值
STATUS_PAGE_FAULT_DEMAND_ZERO
作集或系統工作集
STATUS_SUCCESS
 寫一個唯讀的頁面
 寫一個防護的頁面


STATUS_SUCCESS
將該頁面轉移回到行程工
回傳值
STATUS_IN_PAGE_ERROR
STATUS_PAGE_FAULT_COPY_ON_WRITE
回傳值
STATUS_IN_PAGE_ERROR
回傳值
STATUS_SUCCESS
執行標記為「不可執行」頁面中的程式碼
STATUS_PAGE_FAULT_GUARD_PAGE
STATUS_PAGE_FAULT_TRANSITION
STATUS_ACCESS_VIOLATION
STATUS_IN_PAGE_ERROR
寫一個標記為「寫時複製」頁面
10
註解
APC: Asynchronous Procedure
Call,屬於 IRQL 中斷層級,用於實
現延遲和異步的過程調用。詳細請
參考第5.2.2節
11
轉移位元
原型位元
分頁檔轉移
0
0
12 11
31
保護
10 9
PFN*
0
54
0
1
位於分頁檔
轉移位元
原型位元
0
分頁框架編號
(PFN)
31
12 11
1
0
10 9
保護
Cd Wt U/S W
5
4
3
2
0
1
31
0
0
1
0
未知原因錯誤
頁面正在轉移
12
13
 原型PTE
 檢查如果原型PTE表示了頁面尚在記憶體中,即原型PTE是
有效的,或在轉移狀態,則可以先將原型PTE變成有效,然
後設置實際的PTE
 否則,呼叫MiResolveProtoPteFault()函式根據情況進行
處理,然後呼叫MiCompleteProtoPteFault()函式把已經有
效的原型PTE影響到真正的PTE
 正在轉移的PTE
 要求零頁面的PTE
 在分頁檔中的PTE
14
 原型PTE
 正在轉移的PTE
 呼叫MiResolveTransitionFault()函式,把正在
轉移的頁面從它所在的串列中移除,並重新設置
PTE,使它變成一個有效的PTE
 要求零頁面的PTE
 在分頁檔中的PTE
15
 原型PTE
 正在轉移的PTE
 要求零頁面的PTE
 呼叫MiResolveDemandZeroFault()函式向系統要
一個記憶體頁面,然後設置好PFN資料庫中對應該
頁面的項目,以及出錯虛擬位址的硬體PTE
 在分頁檔中的PTE
16
 原型PTE
 正在轉移的PTE
 要求零頁面的PTE
 在分頁檔中的PTE
 呼叫MiResolvePageFileFault()函式將出錯頁面的
PTE設置為正在轉移以及讀取操作正在進行。該函式
不會發起I/O,而只是填充好這些資訊,放在輸出參
數的ReadBlock中
17
 各種錯誤的輔助處理函式最後都會回傳結果訊
息給MiDispatchFault()函式作後續處理,它會
根據回傳的指示決定是否需要呼叫IoPageRead()
函式發出I/O請求,並且針對I/O成功與否作相
關的處理。最後,MiDispatchFault()也會負責
釋放資源以及維護鎖的一致性
18
 在本章節裡面,我們探討了:
 有哪些可能導致分頁錯誤的情形
 MmAccessFault() 函式如何透過剖析 PDE 和
PTE 判斷分頁錯誤屬於哪一種情形
 MiDispatchFault() 函式如何針對各種分頁錯誤
情形進行處理
19
2012/6/13
郭星辰
姜智文
20
 現代作業系統的一個重要特性
 機制
父行程、子行程為獨立的分頁表,但分頁表指向相同
的實體頁面(唯讀)
2. 當行程寫數據,記憶體管理員複製頁面,父行程、子
行程擁有私有頁面(可讀寫)
1.
21
 將複製的動作延遲到真正需要兩個行程分配各自私有
頁面的時候,避免不必要的資料複製,減緩了對記憶
體的需求
22
 記憶體區段物件創建函數NtCreateSection 和
MmCreateSection參數SectionPageProtection指定了記
憶體區段物件中頁面的保護屬性
 PAGE_READ (讀)
 PAGE_READWRITE (完全共用,只有一個)
 PAGE_WRITECOPY (寫時複製)
23
NtCreateSection
MmCreateSection
分頁檔支撐的記憶體集區
MiCreatePagingFileMap
映像檔記憶體集區
MiCreateImageFileMap
資料檔案記憶體集區
MiCreateDataFileMap
MiSectionInitialization
建立實體記憶體區物件
保護屬性由
MmCreateSection參數
SectionPageProtection
經
MiMakeProtectionMask
函式變化而來,
變
為MM_WRITECOPY
此函式建立的映像檔記
憶體區
皆為
的,除非SECTION
有特別設定不相容的保
護屬性
頁面保護屬性為
MM_EXECUTE_READ
WRITE,可讀寫、可執
行、
24
 PTE資料結構MMPTE_HARDWARE中,第9位元是一
個保留位元,Windows將他解釋成CopyOnWrite位元
25
 階段一: 一個寫時複製的頁面被第一次分配
 第一次存取頁面時,硬體PTE無效
 MiCompleteProtoPteFault呼叫巨集來 填充PTE的位元
 定義中,寫時複製保護屬性為0x200,即PTE中的第9位
元 設置為1,而寫位元(第1位元)設置0,發生分頁錯誤
26
 階段二:一個行程對一個支援寫時複製的頁面執行了
寫入操作,觸發一個分頁錯誤
 MmAccessFault檢測錯誤 → 呼叫MiCopyOnWrite
1. 根據參數中指定的出錯位址找到PFN資料庫中對應的項,印證他是
一個原型PTE
2. 透過MiRemoveAnyPage申請一個實體頁面
3. 呼叫MiInitializeCopyOnWritePfn初始化其PFN對應的項
4. 從系統PTE區域中申請一個空閒PTE,完成記憶體頁面的複製
5. 填好出錯位址PTE項目
6. 舊頁面PFN資料庫中計數減一
27
28
2012/6/13
黃詩蘋
姜智文
29
typedef struct _MMPFN {
union {
PFN_NUMBER Flink;
WSLE_NUMBER 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;
};
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 :
MI_PFN_PRIORITY_BITS;
ULONG_PTR MustBeCached : 1;
};
} u4;
} MMPFN, *PMMPFN;
Extern PMMPFN MmPfnDatabase;
# define MI_PFN_ELEMENT (index)
(&MmPfnDatabase[index])
30
1.活動狀態 (active)
•頁面正在被某個行程使用,或者被用
於系統空間(非分頁集區,或者在系
統工作集)
•有效的PTE指向頁面
2.備用狀態 (standby)
•原屬於某個行程或系統,但現在已從
工作集中移除
•頁面的內容尚未被修改
•原PTE  正在轉移的無效PTE
3.已修改狀態 (modified) •頁面的內容已被修改過
•原PTE  正在轉移的無效PTE
4.已修改但不寫出
(modified no-write)
•類似於3.,但差別在於,記憶體管理
員不會將內容寫到磁碟上
31
5. 轉移狀態
•頁面正進行I/O操作
•若發生分頁錯誤時,可透過此狀態判斷出衝突
的情形
6. 空閒狀態
•頁面是空閒的,不屬於任何一個工作集
7. 零化狀態
•內容歸零,也不屬於任何一個工作集
8. 壞狀態
•產生硬體錯誤
32
請問實體頁面的狀態有哪些呢?
請列出五種來吧ˊ_>ˋ
33
union {
PKEVENT Event;
NTSTATUS ReadStatus;
} u1;
union {
PMMPTE PteAddress;
WSLE_NUMBER WsIndex;
union {
} u1;
ULONG_PTR ShareCount;
PMMPTE PteAddress;
} u2;
union {
union {
union {
ULONG_PTR ShareCount;
struct {
PFN_NUMBER Flink;
} u2;
USHORT ReferenceCount;
} u1;
union {
MMPFNENTRY e1;
PMMPTE PteAddress;
struct {
};
union {
USHORT ReferenceCount;
} u3;
PFN_NUMBER Blink;
MMPFNENTRY e1;
MMPTE OriginalPte;
} u2;
};
union {
union {
} u3;
struct {
struct {
MMPTE OriginalPte;
USHORT ReferenceCount;
ULONG_PTR PteFrame: 25;
union {
MMPFNENTRY e1;
ULONG_PTR InPageError : 1;
struct {
};
ULONG_PTR VerifierAllocation : 1;
ULONG_PTR PteFrame: 25;
} u3;
ULONG_PTR AweAllocation : 1;
MMPTE OriginalPte;
ULONG_PTR InPageError : 1;
ULONG_PTR Priority : 3;
ULONG_PTR VerifierAllocation : 1; union {
ULONG_PTR MustBeCached : 1;
struct {
ULONG_PTR AweAllocation : 1;
};
ULONG_PTR PteFrame: 25;
ULONG_PTR Priority : 3;
} u4;
ULONG_PTR InPageError : 1;
ULONG_PTR MustBeCached : 1;
ULONG_PTR VerifierAllocation : 1;
};
ULONG_PTR AweAllocation : 1;
(d)轉移狀態的PFN項目
} u4;
ULONG_PTR Priority : 3;
ULONG_PTR MustBeCached : 1;
(a)活動狀態頁面的PFN項目
};
} u4;
(b)備用狀態或已修改狀態的PFN項目
(c)歸零的或空閒頁面的PFN項目
34
typedef struct _MMPFNENTRY {
頁面已被修改
USHORT Modified : 1 ;
USHORT ReadInProgress : 1 ; 正進行”讀取”或”寫入”操作
USHORT WriteInProgress : 1 ;
原型PTE
USHORT PrototypePte : 1 ;
USHORT PageColor : 4 ;
頁面狀態
USHORT PageLocation : 3 ;
USHORT RemovalRequested : 1 ;
USHORT CacheAttribute : 2
USHORT Rom : 1 ;
USHORT ParityError : 1 ;
} MMPFNENTRY ;
35
36
2012/6/13
余修丞
姜智文
37
 記憶體管理員根據「系統記憶體的數量」以及各個
「行程對於記憶體的需求」,動態調度實體頁面的使
用
 ex: 當一行程需要記憶體,記憶體管理員可以從「零化
串列」或「空閒串列」中找到頁面滿足行程需求
 行程結束,記憶體管理員回收行程的頁面
38
 記憶體管理員動態調度的過程,頁面可能會經歷各種
變化
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
39
 記憶體管理員動態調度的過程,頁面可能會經歷各種
變化
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
40
 為解決分頁錯誤,行程會需要一個頁面,它有可能從
圖中的4個串列其中之一取得實體記憶體頁面
歸零串列
空閒串列
備用串列
修改串列
41
 從修改或備用串列中取得實體頁面
分頁錯誤由正在轉移狀態的無效PTE引起
PTE
1.尋找
分頁框架編號
實體頁面
PFN資料庫項目
2.呼叫
3. 該頁面從
串列中移除
備用串列
修改串列
MiUnlinkPageFromL
ist函式
42
 從空閒(零化或備用)串列取得實體頁面:
分頁錯誤由一位於分頁檔中的PTE所引起,分頁錯
誤處理常式會透過MiRemoveAnyPage函式獲得一
實體頁面滿足分頁錯誤。
 從零化(空閒或備用)串列取得實體頁面:
若分頁錯誤需要零頁面來滿足此錯誤,則呼叫
MiRemoveZeroPage函式獲得一零化頁面
43
 記憶體管理員動態調度的過程,頁面可能會經歷各種
變化
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
44
 空閒串列 -(零頁面緒程)-> 零化串列
- MmZeroPageThread函式
45
 MmZeroPageThread函式是個無限迴圈,他等待以下
兩個事件之一: MmZeroingPageEvent和
PoSystemIdelTimer
MiInsertPageInFreeList
函式
1.插入頁面
空閒串列
MmZeroPageThread
2. 空閒串列頁面數大於
MmMinimumFreePageToZero
=8
3. 通知
零化事件
MmZeroingPageEvent
46
 零化操作
零化事件
MmZeroingPageEvent
1.通知
MmZeroPageThrea
d
3. 呼叫
2. 取得頁面
設定成壞頁面
指定為零
4. 將頁面插入至
MmZeroPageListHead
MiInsertPageInList
函式
空閒串列
重複以上步驟,直至目前空閒串列頁面全部零化
47
 記憶體管理員動態調度的過程,頁面可能會經歷各種
變化
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
48
 備用串列-(必要時)->空閒串列
一般情況下,備用串列頁面不會主動地轉移到空閒串
列中,除非這些頁面已經確定不被使用了。
 發生轉移的例子:
當行程刪除一段記憶體時,包括行程、程序結束時清
除記憶體做的操作
(發生在MiDeleteVirtualAddresses函式中)
49
 MiDeleteVirtualAddresses函式被呼叫到,會把不
再共用的有效頁面轉移到空閒串列中,也就是從
工作集刪除頁面,見圖4.29
 此過程發生如下:
MiDeleteVirtualAddre
sses
函式
工作集
轉移頁面
空閒串列
共用計數器減1為0
引用計數器為1
1. 呼叫
MiDeletePte
函式
MiDecrementShareCou
nt函式
2. 呼叫
共用計數器
引用計數器
50
 當可用行程記憶體數量很少時,記憶體管理員修剪行
程或系統工作集,這工作由
MmWorkingSetManager(工作集管理員)函式的元件完
成
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
51
 記憶體管理員動態調度的過程,頁面可能會經歷各種
變化
修剪”髒”頁面
修改串列
“軟”錯誤
工作集
(行程或系統)
“軟”錯誤
“軟”錯誤
刪除頁面
“硬”錯誤
歸零串列
修改頁面
寫出器
修剪乾淨頁面
備用串列
空閒串列
必要時
零頁面程序
52
 修改串列 -(修改頁面寫出器)->備用串列
隨著工作集的頁面被加到修改串列中,修改串列會變
大。到一定程度,記憶體管理員要將修改串列的頁面
寫到磁碟上,進而把頁面轉移到備用串列,是利用修
改頁面寫出器來完成
53
2012/6/13
吳藺剛
54
 在MMPFN資料結構的u3.e1成員,有一個4位元的
PageColor欄位,代表一個PFN項目的顏色
Typedf struct _MMCOLOR_TABLES{
PFN_NUMBER Flink;
PVOID Blink;
PFN_NUMBER Count;
}MMCOLOR_, *PMMCOLOR_TABLES;
Extern PMMCOLOR_TABLES MmFreePagesByColor[2];
55
 初始化在MiInitMachineDependent函式,與
MmSecondaryColors和MmSecondaryColorsMask有關
 例如MmFreePagesByColor[0][0]代表顏色為0的零化頁
面串列,MmFreePagesByColor[1][0]代表顏色為0的空
閒頁面串列開頭
56
MiInsertPageInFreeList
00638 /* Get the last page on the list */
00639 LastPage = ListHead->Blink;
00640 if (LastPage != LIST_HEAD)
00641 {
00642
/* Link us with the previous page, so we're at the end now */
00643
MI_PFN_ELEMENT(LastPage)->u1.Flink = PageFrameIndex;
00644 }
00645 else
00646 {
00647
/* The list is empty, so we are the first page */
00648
ListHead->Flink = PageFrameIndex;
00649 }
00650
00651 /* Now make the list head point back to us (since we go at the end) */
00652 ListHead->Blink = PageFrameIndex;
00653 ASSERT_LIST_INVARIANT(ListHead);
00654
00655 /* And initialize our own list pointers */
00656 Pfn1->u1.Flink = LIST_HEAD;
00657 Pfn1->u2.Blink = LastPage;
57
00682 /* Get the page color */
00683 Color = PageFrameIndex & MmSecondaryColorMask;
00684
00685 /* Get the first page on the color list */
00686 ColorTable = &MmFreePagesByColor[FreePageList][Color];
00687 if (ColorTable->Flink == LIST_HEAD)
00688 {
00689
/* The list is empty, so we are the first page */
00690
Pfn1->u4.PteFrame = COLORED_LIST_HEAD;
00691
ColorTable->Flink = PageFrameIndex;
00692 }
00693 else
00694 {
00695
/* Get the previous page */
00696
Blink = (PMMPFN)ColorTable->Blink;
00697
00698
/* Make it link to us, and link back to it */
00699
Blink->OriginalPte.u.Long = PageFrameIndex;
00700
Pfn1->u4.PteFrame = MiGetPfnEntryIndex(Blink);
00701 }
00702
00703 /* Now initialize our own list pointers */
00704 ColorTable->Blink = Pfn1;
58
MiInsertPageInFreeList
00868 /* Zero pages go to the head, all other pages go to the end */
00869 if (ListName == ZeroedPageList)
00870 {
00871
/* Make the head of the list point to this page now */
00872
Flink = ListHead->Flink;
00873
ListHead->Flink = PageFrameIndex;
00874
00875
/* Make the page point to the previous head, and back to the list */
00876
Pfn1->u1.Flink = Flink;
00877
Pfn1->u2.Blink = LIST_HEAD;
00878
00879
/* Was the list empty? */
00880
if (Flink != LIST_HEAD)
00881
{
00882
/* It wasn't, so update the backlink of the previous head page */
00883
Pfn2 = MI_PFN_ELEMENT(Flink);
00884
Pfn2->u2.Blink = PageFrameIndex;
00885
}
00886
else
00887
{
00888
/* It was empty, so have it loop back around to this new page */
00889
ListHead->Blink = PageFrameIndex;
00890
}
59
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
/* Get the page color */
Color = PageFrameIndex & MmSecondaryColorMask;
/* Get the list for this color */
ColorHead = &MmFreePagesByColor[ZeroedPageList][Color];
/* Get the old head */
Flink = ColorHead->Flink;
/* Make this page point back to the list, and point forwards to the old head */
Pfn1->OriginalPte.u.Long = Flink;
Pfn1->u4.PteFrame = COLORED_LIST_HEAD;
/* Set the new head */
ColorHead->Flink = PageFrameIndex;
60
1.在MiRemovePageFromList函式中,先從指定的串列開頭移除一個頁面,然後更新顏色串列。
2.在MiRemovePageByColor函式中,根據指定顏色和頁面PFN從串列開頭移除頁面,並更新顏色串列
00364
00365
00366
00367
/* Get the PFN entry */
Pfn1 = MI_PFN_ELEMENT(PageIndex);
ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
ASSERT(Pfn1->u3.e1.Rom == 0);
00379
00380
/* Remove a page */
ListHead->Total--;
00414 /* Zero flags but restore color and cache */
00415 Pfn1->u3.e2.ShortFlags = 0;
00416 Pfn1->u3.e1.PageColor = OldColor;
00417 Pfn1->u3.e1.CacheAttribute = OldCache;
00418
00419 /* Get the first page on the color list */
00420 ASSERT(Color < MmSecondaryColors);
00421 ColorTable = &MmFreePagesByColor[ListName][Color];
00422 ASSERT(ColorTable->Count >= 1);
00423
00424 /* Set the forward link to whoever we were pointing to */
00425 ColorTable->Flink = Pfn1->OriginalPte.u.Long;
61
 用法:MMPFN用三位元描述一個頁面的優先順序0-7,
有8個備用串列飛別存放各個優先順序的頁面
EX:MmStandByPageListByPriority
 在WRK中,備用串列MmStandbyPageListHead雖然有
定義,但並沒有真實用到,真正的備用串列是
MmStandybyPageListByPriority陣列
62
1.MiInsertStandbyListAtFront
00743 /* Grab the PFN and validate it is the right kind of PFN being inserted */
00744 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
00745 ASSERT(Pfn1->u4.MustBeCached == 0);
00746 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
00747 ASSERT(Pfn1->u3.e1.PrototypePte == 1);
00748 ASSERT(Pfn1->u3.e1.Rom != 1);
00749
00750 /* One more transition page on a list */
00751 MmTransitionSharedPages++;
00752
00753 /* Get the standby page list and increment its count */
00754 ListHead = &MmStandbyPageListByPriority [Pfn1->u4.Priority];
00755 ASSERT_LIST_INVARIANT(ListHead);
00756 ListHead->Total++;
00757
00758 /* Make the head of the list point to this page now */
00759 Flink = ListHead->Flink;
00760 ListHead->Flink = PageFrameIndex;
00761
00762 /* Make the page point to the previous head, and back to the list */
00763 Pfn1->u1.Flink = Flink;
00764 Pfn1->u2.Blink = LIST_HEAD;
63
2.在MiInsertPageList函式中,即使參數中只訂了MmStandbyPageListHead作為串列開頭,真正的
目標串列開頭也要轉變成MmStandbyPageKistByPriority陣列中對應優先順序的串列。
00838
00839
00840
00841
00842
00843
00844
/* Standby pages are prioritized, so we need to get the real head */
if (ListHead == &MmStandbyPageListHead)
{
/* Obviously the prioritized list should still have the same name */
ListHead = &MmStandbyPageListByPriority[Pfn1->u4.Priority];
ASSERT(ListHead->ListName == ListName);
}
64
在MiRemovePageFromList函式中,如果要移除備用頁面,客戶程式碼不能指
定MmStandbyPageListHead作為備用串列,而必須是
MmStandbyPageListByPriority陣列的一個成員。MiRestoreTransitionPte函
式中,移除一個節點,對於一個備用頁面,其對應的PTE會處於轉移狀態,所以
頁面移除後,須恢復對應PTE的內容,而不再處於轉移狀態
00254
00255
00256
00257
00258
00269
// Remove page, but leave its status as idle.
Pfn->u3.e1.Location = StandbyPageList;
MiRestoreTransitionPte (PageFrameIndex);
MiInsertPageInList (MmPageLocationList[BadPageList],PageFrameNumber);
return;
65
在MiUnlinkPageFromList函式中,如果根據頁面PFN項目得到的串列是
MmStandbyPageListHead,則將串列轉換為MmStandbyPageListByPriority
陣列中對應的串列
00247 /* Check if this was standby, or modified */
00248 if (ListHead == &MmStandbyPageListHead)
00249 {
00250
/* Should not be a ROM page */
00251
ASSERT(Pfn->u3.e1.Rom == 0);
00252
00253
/* Get the exact list */
00254
ListHead = &MmStandbyPageListByPriority[Pfn->u4.Priority];
00255
00256
/* See if we hit any thresholds */
00257
if (MmAvailablePages== MmHighMemoryThreshold)
00258
{
00259
/* Clear the high memory event */
00260
KeClearEvent(MiHighMemoryEvent);
00261
}
00262
else if (MmAvailablePages== MmLowMemoryThreshold)
00263
{
00264
/* Signal the low memory event */
00265
KeSetEvent(MiLowMemoryEvent, 0, FALSE);
00266
}
66
 分成兩部分:
 頁面PFN項目的OriginalPre.u.Soft.Prototype位元欄位為
0則該頁面被置於MmModifiedPageListByColor[0]中,
外部記憶體位置是分頁檔
 其餘頁面被放在全域串列MmModifiedPageListHead中,
這些頁面的外部記憶體位置是對應檔案
 MmModifiedPageListHead中的計數值也包括位於分
頁檔的那些頁面
 修改頁面的插入透過MiInsertPageInList,移除透過
MiUnlinkPageFromList
67
 該串列的頁面被修改了,但記憶體管理員的修改頁面
寫出器並不會把這些頁面寫到外部記憶體
 透過MiInsertPageInList可在該串列尾部插入頁面,透
過MiInsertFrontNodifiedNoWrite可在該串列前端插
入一個頁面, 頁面移除則要靠MiUnlinkPageFromList
完成
 MmEnableModifiedWriteOfSection
 U.Flags.NoModiefiedWriting旗標位元
68
MmEnableModifiedWriteOfSection
4657
// Move any straggling pages on the modnowrite list back onto the modified list
otherwise they would be orphaned and this could cause us to run out of pages.
4658 if (MmModifiedNoWritePageListHead.Total != 0) {
4659
PageFrameIndex = MmModifiedNoWritePageListHead.Flink;
4660
do {
4661
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
4662
NextPageFrameIndex = Pfn1->u1.Flink;
4663
Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
4664
if (ControlArea == Subsection->ControlArea) {
4665
// This page must be moved to the modified list.
4666
MiUnlinkPageFromList (Pfn1);
4667
MiInsertPageInList (&MmModifiedPageListHead,PageFrameIndex);
4668
}
4669
PageFrameIndex = NextPageFrameIndex;
4670
} while (PageFrameIndex != MM_EMPTY_LIST);
69
 NTFS檔案系統利用這種頁面來對應檔案系統中繼資料,
所以,這些資料不會自動寫上磁碟上,當紀錄中繼資
料修改情況的日誌資訊被寫到磁碟上後,才允許中繼
資料寫上
 保證中繼資料的修改均有紀錄,若發生意外,中繼資
料可以恢復
 副作用是這些頁面將占據系統實體記憶體,所以客戶
有責任確保不會累積太多,且透過
MmEnableModifiedWriteOfSection將他移除
70
 相對簡單,並不參與系統頁面調度,透過
MiInsertPageInList可以將一個壞頁面插入
MmBadPageListHead串列
 若零化、空閒或備用頁面的PFN項目的
u3.e1.RemovalReaquested位元欄,被設定,則被插入
到會頁面串列
 壞頁面插入後就不再移除
71
00549 /* Check the colored zero list */
00550 PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
00551 if (PageIndex == LIST_HEAD)
00552 {
00553
/* Check the zero list */
00554
ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
00555
PageIndex = MmZeroedPageListHead.Flink;
00556
if (PageIndex == LIST_HEAD)
00557
{
00558
/* This means there's no zero pages, we have to look for free ones */
00559
ASSERT(MmZeroedPageListHead.Total == 0);
00560
Zero = TRUE;
00561
00562
/* Check the colored free list */
00563
PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
00564
if (PageIndex == LIST_HEAD)
00565
{
00566
/* Check the free list */
00567
ASSERT_LIST_INVARIANT(&MmFreePageListHead);
00568
PageIndex = MmFreePageListHead.Flink;
00569
Color = PageIndex & MmSecondaryColorMask;
00570
ASSERT(PageIndex != LIST_HEAD);
00571
if (PageIndex == LIST_HEAD)
00572
{
00573
/* FIXME: Should check the standby list */
00574
ASSERT(MmZeroedPageListHead.Total == 0);
00575
}
00576
}
00577
}
00578
else
00579
{
00580
Color = PageIndex & MmSecondaryColorMask;
00581
}
00582 }
72
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
/* Check the colored free list */
PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
if (PageIndex == LIST_HEAD)
{
/* Check the colored zero list */
PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
if (PageIndex == LIST_HEAD)
{
/* Check the free list */
ASSERT_LIST_INVARIANT(&MmFreePageListHead);
PageIndex = MmFreePageListHead.Flink;
Color = PageIndex & MmSecondaryColorMask;
if (PageIndex == LIST_HEAD)
{
/* Check the zero list */
ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
PageIndex = MmZeroedPageListHead.Flink;
Color = PageIndex & MmSecondaryColorMask;
ASSERT(PageIndex != LIST_HEAD);
if (PageIndex == LIST_HEAD)
{
/* FIXME: Should check the standby list */
ASSERT(MmZeroedPageListHead.Total == 0);
}
73
 Windows提供用來描述實體記憶的資料結構
Typedef struct _MDL{
Struct _MDL *Next;
CSHORT Size;
CSHIRT MdlFlags;
Struct _EORICESS *Process;
POVID MappedSystemVa;
ULONG ByteCount;
ULONG ByteOffset;
}MDL, *PMDL
 存取到包含這些頁面的PFN
Page=(PPFB_NUMBER)(Mdl+1);
74
 PMDL
MmallocatePageForMdl(
__in PHYSICAL_ADDRESS LowAddress,
__in PHYSICAL_ADDRESS HighAddress,
__in PHYSICAL_ADDRESS SkipBytes,
__in Size_T TotalBytes
);
75
 PMDL
MmAllocatePagesForMdlEx(
__in PHYSICAL_ADDRESS LowAddress,
__in PHYSICAL_ADDRESS HighAddress
__in Size_T TotalBytes,
__in MEMORY_CACHING_TYPE CacheType,
__in ULONG Flags
);
76
 VOID
MmFreePagesFroMdl(
__in PMDL MemoryDescriptorList
);
77
 為在指定的位址範圍內獲得足夠的記憶體,
MiAllocatePagesForMDl函式搜尋零化、空閒和備用串
列,盡可能滿足的記憶體需求。但函式回傳時,實際
申請到的記憶體頁面數有可能少於客戶要求數量,所
以必須檢查結果MDL物件中的成員值
 MiAllocatePagesForMdl並不將這些實體記憶體對應到
虛擬位址空間,若需要虛擬位址,則須自己對應
 MmFreePagesFromMdl函式負責把實體記憶體歸還
PFN資料庫
78
2012/6/13
洪國勛
劉乃菀
79
 當系統memory消耗很多,零化序列、空閒序列和備
用序列上的page數會減少,當少到一定程度時,修改
頁面寫出器(modified page writer)就會啟動,將已修
改的頁面寫到外部記憶體中
 Modified page writer主要做兩種工作:
 負責往「分頁檔」中寫頁面
 負責往「對應檔案」中寫頁面
 一旦頁面內容被寫到外部記憶體,這些頁面就會被轉
移到備用序列中,供系統或其他Process使用
80
 當記憶體管理員初始化時,MmInitSystem函式會建立
一個系統緒程,其啟動程序為MiModifiedPageWriter ,
modified page writer在即此實作
 MiModifiedPageWriter 執行後會建立第二個緒程
MiMappedPageWriter
 此兩個緒程都屬於即時優先順序,有較高優先權
81
初始化
啟動對應頁面寫出緒程
等待事件訊號
無窮迴圈等待事件
呼叫MiModifiedPageWriterWorker函式
分頁檔的寫入操作
呼叫MiGatherPagefilePages函式
事件觸發
對應檔案頁面寫入操作
呼叫MiGatherMappedPages函式
82
 使用PsCreateSystemThread函數創建一個新的
SystemThread
InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
if (!NT_SUCCESS(PsCreateSystemThread (
&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes, 0L,
NULL,
MiModifiedPageWriter, /* 啟動的函式 */
NULL))) {
return FALSE;
}
83
/* Initialize listheads as empty. */
MmSystemShutdown = 0;
KeInitializeEvent (&MmPagingFileHeader.Event, NotificationEvent, FALSE);
KeInitializeEvent (&MmMappedFileHeader.Event, NotificationEvent, FALSE);
InitializeListHead(&MmPagingFileHeader.ListHead);
InitializeListHead(&MmMappedFileHeader.ListHead);
InitializeListHead(&MmFreePagingSpaceLow);
MmNumberOfMappedMdls = MmNumberOfPhysicalPages / (32 * 1024);
/* Allocate enough MDLs */
if (MmNumberOfMappedMdls < 20) {
MmNumberOfMappedMdls = 20;
}
84
InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
Status = PsCreateSystemThread (
&ThreadHandle,
THREAD_ALL_ACCESS,
&ObjectAttributes,
0L,
NULL,
MiMappedPageWriter, /* 啟動的函式 */
NULL);
ZwClose (ThreadHandle);
MiModifiedPageWriterWorker (); /* 啟動MiModifiedPageWriterWorker */
85
 MmModifiedPageWriterEvent
 觸發條件(擇一)
 已修改過的page數超過預設上限
 可用page數少於256且修改串列中的page數達到預設上限
 觸發後應獲得信號(擇一)
 寫入「分頁檔」
 寫入「對應檔」
 MiMappedPagesTooOLdEvent
 每隔300秒觸發一次
 防止對應檔案中的資料修改之後由於系統當機等意外事
故造成大量資料未能及時刷新到外部記憶體
86
已修改過的page
if (MmAvailablePages < MM_PLENTY_FREE_LIMIT) {
數之預設上限
/* If necessary, start the modified page writer. */
if (MmModifiedPageListHead.Total >= MmModifiedPageMaximum) {
KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
}
可用page數
else if ((MmAvailablePages < MM_TIGHT_LIMIT) &&
之預設上限
(MmModifiedPageListHead.Total >= MmModifiedWriteClusterSize)) {
KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
修改串列中的page
}
數之預設上限
}
87
start
掃描分頁檔點陣圖
尋找連續空
閒的page
檢查修改串列中的已修改頁面
其PTE設置於分頁檔中
設置PNF項目的寫入旗標位元
呼叫IoAsynchronousPageWrite函式
88
 發出寫入操作
/* Issue the write request. */
Status = IoAsynchronousPageWrite (File,
&ModWriterEntry->Mdl,
&StartingOffset,
MiWriteComplete,
(PVOID)ModWriterEntry,
IrpPriority,
&ModWriterEntry->u.IoStatus,
&ModWriterEntry->Irp);
指定的完成函式
89
start
檢查MmModifiedPageListHead串列中的頁面
欲找出盡可能多的
針對同一對應檔案
的已修改頁面
檢查與之相鄰的原型PTE
將其插入到MmMappedPageWriterList中
設置事件MmModifiedPageWriterEvent有信號
通知對應頁面寫出器緒程
90
2012/6/13
黃柏軒
劉乃菀
91
 主函式KeSwapProcessOrStack,主要執行下列事項:
 換出Kernel Stack <KiOutSwapKernelStacks>
 換出Process <KiOutSwapProcesses>
 換入Process <KiInSwapProcesses>
 換入Kernel Stack <KiInSwapKernelStacks>
92
 記憶體管理員每隔4或8個他自身的檢查週期(1秒鐘),
就會設置事件的訊號
 當進行一些有關緒程或行程的操作時,例如終止緒程、
緒程脫離一個行程、就緒一個緒程等情形,若這些操
作有必要換出或換入行程或核心堆疊,也會觸發事件
93
 依序檢查Waiting-In list上的Thread,若發現某個
Thread已經等待超過stack protect time則將該Thread
換出
 執行前要先升高IRQL(Interrupt Request Level)並lock
住資料庫
KiLockDispatcherDatabase(&OldIrql);
94
 Define kernel stack protect time:
 若在小記憶體系統則設為3秒
 其餘系統則設為5x秒
#define SMALL_SYSTEM_STACK_PROTECT_TIME (3 * 75)
#define LARGE_SYSTEM_STACK_PROTECT_TIME
(SMALL_SYSTEM_STACK_PROTECT_TIME * 5)
:
KiStackProtectTime = LARGE_SYSTEM_STACK_PROTECT_TIME;
95
 從等待串列中提取至多5個滿足條件的緒程,並將他們
從等待串列中移除
#define MAXIMUM_THREAD_STACKS 5
:
while ((NextEntry != &Prcb->WaitListHead) &&
(NumberOfThreads < MAXIMUM_THREAD_STACKS)) {
:
}
96
if ( WaitLimit < Thread->WaitTime ) {
break;
}
else if (Thread->Priority >= (LOW_REALTIME_PRIORITY + 9)) {
RemoveEntryList(&Thread->WaitListEntry);
}
else if (KiIsThreadNumericStateSaved(Thread)) {
ThreadObjects[NumberOfThreads] = Thread;
NumberOfThreads += 1;
RemoveEntryList(&Thread->WaitListEntry);
Process->StackCount -= 1;
}
97
 如果某Process的所有Thread都被換出,則該行程會被
放到行程換出串列
if (Process->StackCount == 0)
Process->State = ProcessOutTransition;
 Unlock資料庫並降低IRQL
KiUnlockDispatcherDatabase(OldIrql);
98
 Increment the last processor number.
KiLastProcessor += 1;
if (KiLastProcessor == (ULONG)KeNumberProcessors) {
KiLastProcessor = 0;
}
 執行換出Kernel Stack
while (NumberOfThreads > 0) {
NumberOfThreads -= 1;
Thread = ThreadObjects[NumberOfThreads];
MmOutPageKernelStack(Thread);
}
99
 所謂換出行程,實際上就是依序換出四個頁面:
 工作集串列頁面
 VAD點陣圖頁面
 超空間分頁表頁面
 分頁目錄頁面
 只有當一個Process的工作集已經降到最小,該行程才
會在函式中被換出,否則,工作集管理員會修剪該
Process的工作集,直到被修剪為最小工作尺寸時,該
行程才會再次被觸發換出操作
100
 依次檢查串列中的每一個Process,若該Process的就緒
緒程串列為空,則將之換出
if (NextEntry != &Process->ReadyListHead) {
:
} else {
Process->State = ProcessOutSwap;
MmOutSwapProcess(Process);
}
 若該Process的就緒緒程串列仍然為空,則將之移出記
憶體
Process->State = ProcessOutOfMemory;
101
 對於已被換出的行程,只要他有一個緒程就緒,他就
會被加入到串列中,並依次恢復四個頁面:
 分頁目錄頁面
 超空間分頁表頁面
 VAD點陣圖頁面
 工作集串列頁面
102
 要換入Kernel stack的Threads構成了一個串列
KiStackInSwapListHead
 當一個Thread變成就緒時,若是這個Thread的Kernel
stack不在記憶體中,則該Thread就會被加入到串列中
 函式會依次處理串列中的每一個Thread,執行實際的
換入操作
MmInPageKernelStack(Thread);
103
 只能騰出很有限的實體記憶體,且必須要等到行程工
作集中的其他頁面(除上述所提到的四個頁面)都被修
剪過後才會發生
 實際系統中,Process的換出換入很少發生,一旦發生,
常常意味著系統的記憶體不足以承擔目前的工作負載,
而且系統的效能會急遽退化
104
 交換器只是保證行程能夠執行,但無法解決效能瓶頸
問題
 解決方法
 增加實體記憶體
 減輕工作負載
105
2012/6/13
黃柏軒
劉乃菀
106
當系統的可用記憶體很少或很充裕
通知
Process知道這些事件發生並做出反應
107
MmInitSystem
呼叫
MiInitializeMemoryEvents
108
 條件:可用記憶體低於Threshold
(MmLowMemoryThreshold)
 MmLowMemoryThreshold 變數的值由系統實體記憶
體的數量決定,除非應用系統在登錄中指定
109
 RAM > 1GB
 第一個1GB為32MB
 餘下每1GB增加8MB
 128MB < RAM < 1GB
 第一個128MB為3.1MB(800 pages)
 餘下每128MB增加4MB
 RAM < 128MB
 等於MmPlentyFreeThreshold
 RAM < 63MB = 400 pages,否則 = 800 pages
110
 MmLowMemoryThreshold 的單位需由megabytes 轉
換成 pages
if (MmLowMemoryThreshold != 0) {
MmLowMemoryThreshold *= ((1024 * 1024) / PAGE_SIZE);
}
111
 RAM > 1GB
if (MmNumberOfPhysicalPages > 0x40000) {
MmLowMemoryThreshold = (32 * 1024 * 1024) / PAGE_SIZE;
MmLowMemoryThreshold+=((MmNumberOfPhysicalPages - 0x40000)
>> 7);
}
 例如:當RAM = 4GB
MmLowMemoryThreshold
= 32MB + ((0x100000 – 0x40000) >> 7) * 4K
= 32MB + 3 * 8MB = (8K + 3 * 2K) pages
112
 1GB > RAM > 128MB
else if (MmNumberOfPhysicalPages > 0x8000) {
MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x8000)
>> 5);
}
 例如:當RAM = 512MB
MmLowMemoryThreshold
= 3.1MB + ((0x20000 – 0x8000) >> 5) * 4K
= 3.1MB + 3 * 4MB = (800 + 3 * 1K) pages
113
if (MmLowMemoryThreshold > (64 * 1024 * 1024) / PAGE_SIZE) {
MmLowMemoryThreshold = (64 * 1024 * 1024) / PAGE_SIZE;
}
 If MmLowMemoryThreshold > 16K pages
 That is RAM > 5GB
114
 高記憶體事件Threshold = 低記憶體事件Threshold的3
倍
if (MmHighMemoryThreshold != 0) {
MmHighMemoryThreshold *= ((1024 * 1024) / PAGE_SIZE);
} else {
MmHighMemoryThreshold = 3 * MmLowMemoryThreshold;
}
if (MmHighMemoryThreshold < MmLowMemoryThreshold) {
MmHighMemoryThreshold = MmLowMemoryThreshold;
}
115
2012/6/13
卓卿安
吳政樺
116
 工作集( Working Set)
 是指一個行程目前正在使用的實體頁面的集合
 Windows 中有三個工作集概念
 行程工作集
 系統工作集
 工作階段工作集
117
 每個行程在建立位址空間時會對應一個專門的頁面存放它
的工作集串列
 其位址放於全域變數 MmWorkingSetList 中,型別為
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;
Wsle 是一個指標,實際上指向一個陣列,
其陣列成員型別為 MMWSLE ,描述一個
有效的頁面, MMWSLE型別定義如下頁
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;
FirstFree 成員指向一個空閒串列開
頭,這些項在內部形成一個單串列
MMWSL資料結構中,FirstDynamic 成
員指向第一個可被修改的頁面,故在
MiTrimWorkingSet 函式中可看到,工作
集修剪都從 FirstDynamic 開始
LastEntry 成員指向最後一個使
用項,因此MiTrimWorkingSet
在修剪工作集時絕對不會超過
LastEntry 項
typedef struct _MMWSLENTRY {
ULONG_PTR Valid : 1;
// 頁面是否有效
ULONG_PTR LockedInWs : 1;
ULONG_PTR LockedInMemory : 1;
ULONG_PTR Protection : 5;
ULONG_PTR Hashed : 1;
// 頁面是否已被插入 Hash Table 中
ULONG_PTR Direct : 1;
// 頁面的 PFN 項目的 WsIndex 直接指向此項的索引
ULONG_PTR Age : 2;
// 頁面最近多久沒被訪問
ULONG_PTR VirtualPageNumber : 20;
} MMWSLENTRY;
typedef struct _MMWSLE {
union {
PVOID VirtualAddress;
ULONG_PTR Long;
MMWSLENTRY e1;
} u1;
} MMWSLE;
高20位元是頁面的虛擬位址
低12位元是 MMWSLENTRY 中定義的旗標位
元
 工作集的最小值在行程建立時設定的,
MmCreateProcessAddressSpace函式的第一個參數
MmCreateProcessAddressSpace(
IN ULONG MinimumWorkingSetSize,
IN PEPROCESS NewProcess,
OUT PULONG_PTR DirectoryTableBase
);
 在 4.3.1 節所述,WRK 中預設最小值是 50 (對於超過 32MB
記憶體的系統),最大值是 345。
 當行程使用很多頁面,並且系統確實有足夠的可用頁面時,
行程工作集可以超越原本設定的最大值,但不能超過系統
設定的一個全域最大值 MmMaximumWorkingSetSize,是
在系統初始化就設定好
MmMaximumWorkingSetSize = (WSLE_NUMBER)(MmAvailablePages - 512);
 當系統記憶體吃緊時,而且行程不需用到很多頁面時,工
作集可能降低到最小值以下
• 在 4.5 節中提到,記憶體管理員包含一個工作集管理員,
會定期檢查各個行程的工作集,為了有效的使用實體記憶
體,必要時須修剪占用記憶體較多的行程工作集。
• 在行程 EPROCESS 資料結構 中 VM 成員,其型別為
MMSUPPORT,包含了工作集管理員在判斷是否需要修剪
一個行程所需的各種資訊。
typedef struct _MMSUPPORT {
LIST_ENTRY WorkingSetExpansionLinks;
LARGE_INTEGER LastTrimTime;
MMSUPPORT_FLAGS Flags;
ULONG PageFaultCount;
WSLE_NUMBER PeakWorkingSetSize;
WSLE_NUMBER GrowthSinceLastEstimate;
如果行程允許被修剪,其
EPROCESS 的
Vm.WorkingSetExpansionLinks 會
被加入到一個以全域變數
MmWorkingSetExpansionHead 為
串列開頭的串列中
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;
VOID KeBalanceSetManager (IN PVOID Context) {
switch (Status) {
.....
case WorkingSetManagerEvent:
在記憶體管理員的平衡集管理員緒程中
被呼叫,每隔1秒被觸發1次,或是當可用
的記憶體降低至某一界限時觸發
MmWorkingSetManager();
break;
呼叫MiComputerSystemTrimCriteria
函式以確定是否需要啟動修剪工作集
或刷新工作集年齡
…..
}
}
VOID MmWorkingSetManager (VOID) {
.....
WorkingSetRequestFlags = MiComputeSystemTrimCriteria (&TrimCriteria);
if (WorkingSetRequestFlags != 0) {
MiProcessWorkingSets (WorkingSetRequestFlags, &TrimCriteria);
}
…..
}
ULONG MiComputeSystemTrimCriteria (IN PMMWS_TRIM_CRITERIA Criteria) {
.....
if ((Available <= PlentyFreePages) ||
(MiReplacing == TRUE) ||
(StandbyRemoved >= (Available >> 2))) {
MiRearrangeWorkingSetExpansionList ();
OutFlags = MI_TRIM_ALL_WORKING_SETS;
}
else if (Available < MM_ENORMOUS_LIMIT) {
1. 目前系統可用頁面數Available少
於當前需要的頁面數
2. 目前工作集中,已經有被替換
頁面的記錄
3. 有超過系統可用頁面數四分之
一的頁面被重新循環利用當作備
用頁面
OutFlags = MI_AGE_ALL_WORKING_SETS;
}
else {
OutFlags = 0;
}
.....
}
MmWorkingSetExpansionHead 串列
中的工作集分成不同類別,並按照修
剪優先順序重新排序
EPROCESS的 Vm->Claim 越大,放在
修剪串列的前部,行程閒置時間越長
放在串列前部。
VOID KeBalanceSetManager (IN PVOID Context) {
switch (Status) {
.....
case WorkingSetManagerEvent:
MmWorkingSetManager();
break;
…..
}
呼叫MiProcessWorkingSets函式實際
修剪或刷新工作集年齡
}
VOID MmWorkingSetManager (VOID) {
.....
WorkingSetRequestFlags = MiComputeSystemTrimCriteria (&TrimCriteria);
if (WorkingSetRequestFlags != 0) {
MiProcessWorkingSets (WorkingSetRequestFlags, &TrimCriteria);
}
…..
}
MiDetermineTrimAmount
VOID MiProcessWorkingSets (IN ULONG WorkingSetRequestFlags, IN PMMWS_TRIM_CRITERIA
確定是否需要修剪此 Process 工作
TrimCriteria) {
集以及修剪多老的頁面,並傳回
修剪頁面數量
…..
MiTrimWorkingSet
if (WorkingSetRequestFlags & MI_TRIM_ALL_WORKING_SETS) {
執行修剪工作,在參數中指定:
Trim = MiDetermineTrimAmount (TrimCriteria, VmSupport);
期望從工作集中修剪多少頁面、
工作集資訊,以及修剪多老的頁
MiAgeWorkingSet
if
((Trim
!=
0)
&&
((TrimCriteria->TrimAllPasses
> TrimCriteria->NumPasses) ||
面
更新工作集中頁面的年齡值
(MmAvailablePages < TrimCriteria->DesiredFreeGoal))) {
估計改行程工作集的 Vm->Claim 值
Trim = MiTrimWorkingSet (Trim, VmSupport, TrimCriteria->TrimAge);
MiAgeWorkingSet (VmSupport, TrimCriteria->DoAging, NULL, &TrimCriteria>NewTotalClaim,
&TrimCriteria->NewTotalEstimatedAvailable);
}
}
else if (WorkingSetRequestFlags & MI_AGE_ALL_WORKING_SETS) {
MiAgeWorkingSet (VmSupport, TRUE, &WslesScanned, &TrimCriteria->NewTotalClaim,
&TrimCriteria->NewTotalEstimatedAvailable);
}
WSLE_NUMBER MiTrimWorkingSet (IN WSLE_NUMBER Reduction, IN PMMSUPPORT WsInfo, IN
ULONG TrimAge)
{
判斷搜尋的起始位置
…..
是否在有效頁面內
NumberLeftToRemove = Reduction;
TryToFree = WorkingSetList->NextSlot;
if (TryToFree > LastEntry || TryToFree < WorkingSetList->FirstDynamic) {
TryToFree = WorkingSetList->FirstDynamic;
從TryToFree開始迴圈搜索
}
移除頁面
StartEntry = TryToFree;
while (NumberLeftToRemove != 0) {
預計要刪除的頁面數 = 0時
……
停止
WsleFlushList.FlushIndex[WsleFlushList.Count] = TryToFree;
WsleFlushList.Count += 1;
NumberLeftToRemove -= 1;
將其編號放WsleFlushlist中
……
TryToFree += 1;
}
…..
}
}
MiFreeWsleList
函式只是把被移
WSLE_NUMBER
MiTrimWorkingSet
(IN WSLE_NUMBER Reduction, IN PMMSUPPORT WsInfo, IN
ULONG
TrimAge)
除了頁面在陣列中做了無效的標
{
記,所以如果
將要被釋放的頁面變
…..
MiTrimWorkingSet 函式完成修
成轉移狀態,轉移至
if (WsleFlushList.Count != 0) {
剪工作後,發現工作集串列中存
備用串列或修改串列,
NumberNotFlushed = MiFreeWsleList (WsInfo, &WsleFlushList);
在許多無效的頁面,就會呼叫
並呼叫
NumberLeftToRemove
+= NumberNotFlushed;
MiRemoveWorkingSetPages 壓縮
MiRemoveWlse 函式
……
} 工作集串列,清除無效的資料
將這些頁面移除
……
if (WorkingSetList->FirstDynamic == WsInfo->WorkingSetSize) {
MiRemoveWorkingSetPages (WsInfo);
}
else {
if ((WsInfo->WorkingSetSize + 15 + (PAGE_SIZE / sizeof(MMWSLE))) < WorkingSetList->LastEntry)
_ {
if ((WsInfo->MaximumWorkingSetSize + 15 + (PAGE_SIZE / sizeof(MMWSLE))) <
WorkingSetList->LastEntry ) {
MiRemoveWorkingSetPages (WsInfo);
}
}
}
VOID MiAgeWorkingSet (IN PMMSUPPORT VmSupport, IN LOGICAL DoAging, IN
PWSLE_NUMBER WslesScanned, IN OUT PPFN_NUMBER TotalClaim, IN OUT
PPFN_NUMBER TotalEstimatedAvailable)
{
…..
PointerPte = MiGetPteAddress (Wsle[CurrentEntry].u1.VirtualAddress);
if (MI_GET_ACCESSED_IN_PTE(PointerPte) == 1) {
MI_RESET_WSLE_AGE(PointerPte, &Wsle[CurrentEntry]);
}
else {
MI_INC_WSLE_AGE(PointerPte, &Wsle[CurrentEntry]);
}
…...
}
如果”存取過”位元未被設定,則
代表上次檢查以來此頁面未被存取
過,於是年齡值加1,最大不超過3
判斷頁面 PTE 的”存取
過”位元是否已設定,
如果被設定了,代表這
個頁面從上次檢查以後
已經被存取過,於是年
齡值為0,並清除該位元
• MMWSL 使用雜湊表, 即HashTable 成員,以加速 WSLE
的搜尋過程
 由於 Wsle 和 HashTable 位於行程位址空間的固定位置處,
空間管理方式直接以頁面對應做管理,而不是透過記憶體
集區管理, HashTable 所佔據的頁面也算在工作集內
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;
USHORT UsedPageTableEntries[MM_USER_PAGE_TABLE_PAGES];
ULONG CommittedPageTables[MM_USER_PAGE_TABLE_PAGES/(sizeof(ULONG)*8)]
} MMWSL, *PMMWSL;
 每一項指定了一個從頁面虛擬位址到工作集串列陣列索引
的關聯
• 以下是雜湊表成員項 MMWSLE_HASH 定義
typedef struct _MMWSLE_HASH {
PVOID Key;
WSLE_NUMBER Index;
} MMWSLE_HASH, *PMMWSLE_HASH;
 以簡單的運算作為虛擬位置的對應並且用線性開放定址法
找到空閒的雜湊桶
 利用公式算出 Key ,然後在雜湊陣列中從 Key 這項開
始尋找,直到找到一個空閒項,然後在這一項中插入
該頁面所對應的雜湊項
• MiLocateWsle 函式
 根據參數中提示的索引項目,判斷是否此索引項目指向
此虛擬位址
 若是,則成功找到工作集串列
 若否,則利用參數中指定的虛擬位址在雜湊清單中搜尋
 若找到,則傳回雜湊項目中的 Index
 若找不到,則在工作串列中進行線性搜尋,
直至找到為止,若還是找不到,則回傳失敗
 平衡集管理員(balance set manager)
 系統初始化時建立,是一個系統緒程
 優先順序 16 ,最低的即時優先順序
 主要用途為維持系統記憶體資源的平衡
 平衡集
 所有具備資源分配資格的行程的集合
 概念
 當系統記憶體吃緊時
1.從擁有較多記憶體的行程的工作集中換出一些頁面
2. 把不滿足執行條件的行程排除在平衡集之外
 實作
 基本上是工作集管理員的一個容器,為它提供一個執行
環境(每1秒觸發1次)
 每隔4或8秒啟動行程/堆疊交換器
2012/6/13
吳彥諄
吳政樺
145
目前系統中實體
記憶體使用量和
分頁檔頁面使用
量
系統位置空間翰
記憶體配置結構
4.5.3節中提到的
記憶體串列
146
記憶體總量和可
提供的記憶體量
分頁使用量以及
還有多少分頁
分頁集和非分頁
集記憶體量
147
系統縮目前的行
程樹
所選之行程記憶
體配置結構
所選行程之虛擬
記憶體資訊
所選行程的
工作集資訊
148
GetPerFromanceInfo
系統記憶體
MemMon
GlobalMemoryStatusEx
Module32First
Module32Next
行程記憶體
Heap32First
Heap32Next
Thread32First
Thread32Next
Process32First
Process32Next
CreateToolhelp32Snapshot
149
Minimum supported client
-Windows XP
Minimum supported server
-Windows Server 2003
Header: winbase.h (Windows.h)
<<contant>>
GlobalMemoryStatusEx
<<contant>>
<<use>>
Struct MEMORYSTATUSEX
150
GlobalMemoryStatusEx
typedef struct _MEMORYSTATUSEX {
DWORD dwLength;
DWORD dwMemoryLoad;
DWORDLONG ullTotalPhys;
DWORDLONG ullAvailPhys;
DWORDLONG ullTotalPageFile;
DWORDLONG ullAvailPageFile;
DWORDLONG ullTotalVirtual;
DWORDLONG ullAvailVirtual;
DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;
使用記憶體的
百分比
所有的實體記憶體
可用的實體記憶體
所有的Page File
可用的Page File
所有的虛擬記憶體
可用的虛擬記憶體
擴展的虛擬記憶體
151
152
Minimum supported client
-Windows XP
Minimum supported server
-Windows Server 2003
Header: Psapi.h
<<contant>>
GetPerformanceInfo
<<contant>>
<<use>>
Struct PERFORMANCE_INFORMATION
153
GetPerformanceInfo
系統當下commit多少page
typedef struct _PERFORMANCE_INFORMATION {
DWORD cb;
SIZE_T CommitTotal;
SIZE_T CommitLimit;
SIZE_T CommitPeak;
SIZE_T PhysicalTotal;
SIZE_T PhysicalAvailable;
SIZE_T SystemCache;
SIZE_T KernelTotal;
SIZE_T KernelPaged;
SIZE_T KernelNonpaged;
SIZE_T PageSize;
DWORD HandleCount;
DWORD ProcessCount;
DWORD ThreadCount;
} PERFORMANCE_INFORMATION,
*PPERFORMANCE_INFORMATION;
系統可commit的最大page數量
系統有多少實體的記憶體
以page計算
系統有多少可使用的記憶體
以page計算
系統有多少快取記憶體
The sum of the memory current
in the paged and nonpaged
kernel pools, in pages.
Page大小,以byte計算
Process數量
執行緒述量
154
GetPerFromanceInfo
系統記憶體
GlobalMemoryStatusEx
MemMon
由上述兩個api 以及MemMom每秒掃描一次,可以知道總觀性的
記憶體使用狀況,以及藉由windows.h、psapi.h所提供的資料結構
,可了解更詳細的數值,實作出系統記憶體介面。
155
Module32First
Module32Next
Heap32First
MemMon
行程記憶體
Heap32Next
Thread32First
Thread32Next
CreateToolhelp32Snapshot
Process32First
Process32Next
156
Minimum supported client
-Windows XP
Minimum supported server
-Windows Server 2003
Header: TlHelp32.h
<<contant>>
dwFlags
<<in>>
CreateToolhelp32Snapshot
<<out>>
th32ProcessID
<<in>>
th32ProcessID
成功則回傳一個open
handle
失敗回傳
INVALID_HANDLE_
VALUE
擷取系統目前部分的
狀態
必須為再擷取出的
process之一,並且視
為此觀察區快中的第
一個process
157
Minimum
supported client
-Windows
XP
Minimum
supported server
-Windows
Server 2003
<<use once>>
Module32First
Module32Next
CreateToolhelp32Snapshot
<<use>>
<<use>>
Heap32First
<<use>>
Heap32Next
Header: TlHelp32.h
<<contant>>
struct MODULEENTRY32
struct HEAPENTRY32
struct THREADENTRY32
Thread32First
Thread32Next
Process32First
<<use>>
struct PROCESSNTRY32
Process32Next
158
Heap32Next & Heap32First
指向heap block
typedef struct tagHEAPENTRY32 {
SIZE_T dwSize;
HANDLE hHandle;
ULONG_PTR dwAddress;
SIZE_T dwBlockSize;
DWORD dwFlags;
DWORD dwLockCount;
DWORD dwResvd;
DWORD th32ProcessID;
ULONG_PTR th32HeapID;
} HEAPENTRY32, *PHEAPENTRY32;
指向線性位置的開頭
Process ID
Heap ID
159
Thread32Next & Thread32First
Thread ID
typedef struct tagTHREADENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32, *PTHREADENTRY32;
屬於哪個process ID擁有此
Thread
此Thread的優先權
160
Module32Next & Module32First
Module ID
typedef struct tagMODULEENTRY32 {
Process ID
DWORD dwSize;
DWORD th32ModuleID;
此module的起始位置
DWORD th32ProcessID;
DWORD GlblcntUsage;
此module有多大
DWORD ProccntUsage;
BYTE *modBaseAddr;
DWORD modBaseSize;
此module的名字
HMODULE hModule;
TCHAR szModule[MAX_MODULE_NAME32 + 1];
此module的path
TCHAR szExePath[MAX_PATH]; }
MODULEENTRY32, *PMODULEENTRY32;
161
Process32Next & Process32First
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags; TCHAR
szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
Process ID
Module id
此process擁有幾個執行序
此process的parent id
此process的名字
162
Process id
Module Name
執行緒數量
此Module 的path
163
利用上述的API,就可以輕鬆的偵測到目前系統記憶體狀態,在根據每過
1秒就掃描一次,就可以做到偵測行程記憶體的資訊。
這樣就可以做到這些介面大部分的資訊了!
164
2012/6/13
吳彥諄
吳政樺
165
 這章讓我們學到:
 Windows記憶體管理員的基礎知識
 Ex. 如何管理分頁及非分頁記憶體
 Ex. 如何管理實體記憶體
 記憶體管理員如何有效低管理有限資源並降低管理負擔
 Windows記憶體管理員的最佳化設施
 MemMon的一些實作原理
166
Download