1.08k likes | 1.26k Views
現代系統核心概論. --------------------------------------------------------. Windows 記憶體管理. 第七組 陳威伸 995202006 吳欣倫 995202101 張鈞為 995202069 李佳珉 995202022 陳弘展 995202019 張育銓 995202026 黃龍旺 995202092. Windows 記憶體管理. --------------------------------------------------------. 分頁錯誤處理.
E N D
現代系統核心概論 -------------------------------------------------------- Windows 記憶體管理 第七組 陳威伸 995202006 吳欣倫 995202101 張鈞為 995202069 李佳珉 995202022 陳弘展 995202019 張育銓 995202026 黃龍旺995202092
Windows 記憶體管理 -------------------------------------------------------- 分頁錯誤處理 黃龍旺995202092
4.4.3分頁錯誤處理 (有效-無效 V位元) • 當處理器再轉譯一個位址時,一旦看到無效的PDE或PTE,它就會觸發一個分頁錯誤,並且將控制權交給設定的相關陷阱處理器(_KiTrap0E)。 • _KiTrap0E先把控制權交給記憶體管理員的MmAccessFault函式處理分頁錯誤。 行程分頁目錄 實體記憶體 分頁表 頁面P1 0 P2的PTE PDE P1的PTE 1
4.4.3分頁錯誤處理 • 發生分頁錯誤的情形 • 轉譯虛擬位址時,如果PDE或PTE的V位元為0 • 無效PTE又分為以下一些情形 • 頁面位於分頁檔或對應檔案中 • 存取一個尚在記憶體中但正在轉移過程中的頁面 • 存取一個尚未提交的頁面 • 存取一個要求零的頁面 • 在使用者模式下存取只能在核心模式下才允許存取的頁面 • 寫唯讀或防護的頁面 • 執行的程式碼位於“不可執行”的頁面中 • 寫一個標記為”寫時複製”的頁面 分頁錯誤分成兩類: 位址轉譯不成功與PTE中的存取控制位元檢查沒通過.
4.4.3分頁錯誤處理 • MmAccessFault需要判斷所有這些情形,並且在可能的情況下解決分頁錯誤,然後傳回處理的結果 • 分頁錯誤解決成功 • 存取違例 • 防護頁面違例 • 頁面換入失敗 • 處理層級超過APC_LEVEL之上的分頁錯誤,Windows核心的設計,執行此層級之上的程式碼不允許分頁。 • 處理系統位址空間 • 處理使用者位址空間
4.4.3分頁錯誤處理 • 系統位址空間 • 檢查PDE • 無效-MiCheckPdeForPagedPool • 由於不同行程位址空間的分頁目錄是獨立的,所以,分頁記憶體區的PDE有可能不一致,Windows使用這種在分頁錯誤處理常式中更新PDE的方法來保持分頁記憶體區分頁表對應的一致性。 • 檢查PTE(有效) • MiCheckSystemPteProtection檢查虛擬位址是否落在系統全域設定的PTE對應保護區,若是,則由該函式直接處理此錯誤。 • 檢查位址存取是否合法,不合法的寫入操作,則直接出錯KeBugCheckEx • 執行的權限是否正確,最後設置PTE的髒位元(第6位元).
4.4.3分頁錯誤處理 • 系統位址空間 • 檢查PTE(無效) • 原型為 1 確認存取的虛擬位址不是在非分頁記憶體中,不是的話用MiPteToProto巨集得到原型PTE的指標 • 原型為 0 轉移為 0 保護為 0 則直接出錯(KeBugCheckEx) • 轉移為 1 正在處理轉移的頁面,若寫權限不足則出錯。
4.4.3分頁錯誤處理 • 使用者位址空間 • 檢查PDE 如果PDE無效,則對於零PDE的情形,看是否為防護分頁,否則將它變成一個要求零的PDE,呼叫MiDispatchFault解決PDE無效的問題,若回傳以後PDE扔然無效,則回傳錯誤,否則設置PDE的髒位。 • 檢查PTE(有效) • 檢查 “寫時複製”頁面 MiCopyOnWrite處理多個行程同時共享相同的代碼,當其中一個行程要進行寫入則會影響其他行程的正常運作,為每個有寫入的行程複製一個新的頁面。
4.4.3分頁錯誤處理 • 使用者位址空間 • 檢查PTE(有效) • 檢查“要求零頁面” MiResolveDemandZeroFault,會根據需要申請一個零頁面,以滿足目前的分頁錯誤。 • 檢查是否有 “零PTE” MicheckVirtualAddress,檢查發生錯誤的虛擬位址的VAD 可以找到,用VAD的資訊建構一個PTE 否,則將它解析為一個“要求零頁面”的PTE,並未它分配一個頁面,然後回傳。
4.4.3分頁錯誤處理 • 使用者位址空間 • 檢查PTE(無效) • 正在轉移的無效PTE MiResolveTransitionFault,把正在轉移的頁面從它所在的串列中移除,並重新設置PTE,使它變成一個有效的PTE。 • 要求一個零頁面的無效PTE MiResolveDemandZeroFault,向系統要一個記憶體頁面(透過MiRemoveZeropage或 MiremoveAnyPage),然後設置好PFN資料庫中對應該頁面的項目,以及出錯虛擬位址的硬體PTE。
4.4.3分頁錯誤處理 • 使用者位址空間 • 檢查PTE(無效) • 分頁檔錯誤 MiResolvePageFileFault,雖然依次分頁錯誤只發生在一個頁面上,盡可能地聚集多個相鄰的頁面,以便將它們一起從分頁檔中讀近來,從而提高應用程式的執行效率,然後將出錯頁面的PTE設置為正在轉移.
現代系統核心概論 263-268 995202026 張育銓
4.4.4 Windows的寫時複製 • 寫時複製(copy-on-write)是現代作業系統的一個重要特性 • 父行程、子行程有獨立的位址空間、獨立的分頁表,但分頁表指向相同的實體頁面 • 所有的資料頁面定義成”唯獨”
4.4.4 Windows的寫時複製 • 若要其修改(頁面中的任一位元組),系統就會捕捉到存取違例,再其複製,讓兩個行程有自己私有的頁面,皆為可讀寫 • 將複製的動作延遲到真正需要兩個行程分配各自私有頁面的時候,避免不必要的資料複製,減緩了對記憶體的需求
4.4.4 Windows的寫時複製 • 記憶體區段物件建立NtCreateSection和MmCreateSection中的參數SectionPageProtection指定了記憶體區段物件中頁面的保護屬性 -PAGE_READ(讀) - PAGE_READWRITE(完全共用,只有一個) -PAGE_EXECUTE (只允許執行代碼,讀寫都不行) -PAGE_WRITECOPY(私有複製,copy on write)
4.4.4 Windows的寫時複製 • MmCreateSection -MiCreateImageFileMap 透過此函式建立的映像檔記憶體區預設情形皆為寫時複製的,除非SECTION有特別設定不相容的保護屬性 • MmCreateSection -MiCreateDataFileMap 頁面保護屬性為MM_EXECUTE_READWRITE,可讀寫、可執行不可寫時複製
4.4.4 Windows的寫時複製 • MmCreateSection -MiCreatePagingFileMap 保護屬性是由MmCreateSection的參數SectionPageProtection透過MiMakeProtectionMask函式變化而來,PAGE_WRITECOPY變為MM_WRITECOPY
4.4.4 Windows的寫時複製 • Windows為硬體PTE定義的資料結構MMPTE_HARDWARE中,第9位元是一個保留位元,Windows將他解釋成CopyOnWrite位元
4.4.4 Windows的寫時複製 • 實作過程 1. 考慮一個寫時複製的頁面是如何被第一次分配的 -第一次存取頁面時,硬體PTE是無效的 -MiCompleteProtoPteFault,呼叫巨集來 填充PTE的位元。
4.4.4 Windows的寫時複製 • MiCompleteProtoPteFault - 定義中,寫時複製保護屬性被解釋成0x200,即PTE中的第9位元被設定,而寫為元(第1位元)沒有被設定。發生分頁錯誤
4.4.4 Windows的寫時複製 • 2. 由於一個行程對一個支援寫時複製的頁面執行了寫入操作,觸發一個分頁錯誤 -MmAccessFault檢測 → 呼叫MiCopyOnWrite
4.4.4 Windows的寫時複製 • MiCopyOnWrite 1. 根據參數中指定的出錯位址找到PFN資料庫中對應的項,印證他是一個原型PTE 2. 透過MiRemoveAnyPage申請一個實體頁面 3. 呼叫MiInitializeCopyOnWritePfn初始化其PFN對應的項 4. 從系統PTE區域中申請一個空閒PTE,完成記憶體頁面的複製
4.4.4 Windows的寫時複製 • MiCopyOnWrite 5. 填好出錯位址的PTE項目 6. 將舊頁面在PFN資料庫中的計數減一
4.5 實體記憶體管理 • PFN(Page Frame Number)資料庫 - 管理系統的實體記憶體
4.5.1PFN資料庫 Typedefstruct _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 • }
4.5.1PFN資料庫 • 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
4.5.1PFN資料庫 • Extern, *PMMPFN • #define MI_PFN_ELEMENT(index) (&MmPfnDatabase[index]) (以分頁框架編號為索引) 一個有效的PTE可以快速的找到其頁面的PFN資料庫
4.5.1PFN資料庫 • 每個行程和系統都有一個工作集 (頁面可能屬於,可能不屬於) • 工作集共八種狀態
4.5.1PFN資料庫 • 活動狀態(active) - 也稱有效狀態(vaild),正在被某個行程使用,或是被用於系統空間。 • 備用狀態(standby) -本來屬於某個行程或是系統工作集,但是現在已經從工作集中移除。 -PTE仍然指向,但標記無效 - 系統回收,或是工作集回收
4.5.1PFN資料庫 • 已修改狀態(modified) - 已從原來的工作集中移除 -PTE仍然指向,但標記無效 - 如系統要回收,必須將內容寫到磁碟上 • 已修改但不寫出(modified no-write) - 記憶體管理員不會將內容寫到磁碟上
4.5.1PFN資料庫 • 轉移狀態 - 說明一個頁面正在進行I/O動作 - 兩個緒程並行的在同一個頁面上引發分頁錯誤時,可以正確處理 • 空閒狀態 -不屬於任何工作集 - 重新使用其頁面以前,為了安全考慮清除髒資料
4.5.1PFN資料庫 • 零化狀態 - 頁面是空閒的,且不屬於任何工作集 - 內容全部歸零 • 壞狀態 - 頁面產生硬體錯誤,系統不再使用此頁面 -唯一不為記憶體管理員管理和調度
現代系統核心概論 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.零化頁面的插入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)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)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 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有Priority的備用串列 • MMPFN的資料結構會有3位元的欄位Priority • 因此有八個備用串列分別存放各個優先順序頁面 • 之前說過備用串列:MmStandbyPageListHead, 其實沒使用。 • 而是利用有Priority的備用串列: MmStandbyPageListByPriority
4.5.3備用頁面的插入和刪除1.在串列前端插入備用頁面4.5.3備用頁面的插入和刪除1.在串列前端插入備用頁面 • 根據頁面PFN項目的u4.Priority找串列開頭,將頁面插入串列中 • 而參數MiInsertPageInList()中的指定了MmStandbyPageListHead作為串列開頭 • 例如:00501 ListHead = &MmFreePageListHead; • 但是真正的目標串列開頭也是會轉換成MmStandbyPageListByPriority陣列去對應優先順序的串列,其餘插入串列的方法沒變化
4.5.3備用頁面的插入和刪除2.在串列中移除一個備用頁面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修改但不寫出頁面串列(為單獨處理的串列)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找不到零化顏色串列該顏色的頁面,則移除其他顏色的頁面,並回傳。