二、實體記憶體的管理
核心管理的最小單位是頁面,首先來看物理頁面的表示 MMPFN描述了一個物理頁面所需要的資訊


NextStackPfn指向一個棧的物理頁面,這個欄位用於要刪除或者增加核心棧的空間的時候用於指向下一個物理頁面、PteAddress指向MMPTE結構,代表一個頁表項
MMPFN的u3.e1是一個MMPFN_ENTRY結構

描述這個頁面核心一些資料結構需要的欄位,例如PageColor是代表這個頁面的顏色,至於其他一些成員等見到再進行討論。PfnUsage代表這個物理頁面的用途,型別是一個列舉值
MmPfnDatabase是由MMPFN組成的陣列,稱為PFN資料庫,通過物理頁面的編號來確定陣列的下標,比如0號頁面對應陣列的0號元素,Flink和Blink可以被插入到一個連結串列中,那為什麼已經有資料庫將每一個物理頁的資訊都儲存了,還要用連結串列串在一起呢?這是因為系統維護了幾個連結串列做特定功能使用,這些連結串列用到的成員就是Flink和Blink。
MMPTE_HARDWARE跟體系相關,x86定義如下,可看出跟IA-32的PTE/PDE是一致的


MMPTE用來表示頁表項PTE,但是它也可以用作PDE或者PPE,PXE,如果MMPTE包含一個有效的地址,那麼就使用hard欄位執行MMPTE_HARDWARE,MMU可以用它來完成地址的轉換。上面說到,系統維護了幾個連結串列來做特定用途,連結串列的定義是

成員Flink和Blink就是上文提到PFN的兩個成員,MMLISTS是列舉值,代表這個連結串列的型別
系統維護的分別是

就緒(Standby)、已修改(Modified)、無寫修改(ModifiedNoWrite),空閒(Free),只讀(Rom),損壞(Bad),零化(Zeroed)
並且加入了MmPageLocationList陣列之中,可以在陣列中利用連結串列列舉型別MMLISTS來進行方便的定位。如下所示

上文提到的MMPFN_ENTRY結構體的成員PageLocation就代表物理頁具體儲存在這個陣列的哪個元素

MMCOLOR_TABLES記錄了一個連結串列,可以用來組織已經著色的頁面
有了上述結構體的大概認識,我們來結合具體函式分析這些結構體的使用。分配實體記憶體使用MmAllocPage函式

引數為要申請的記憶體型別,然後返回一個物理頁面編號。獲取PFN的自旋鎖後,從零化頁面找出一個物理頁號(並在原零化連結串列中移除),然後根據這個頁號在PFN資料庫中獲取具體的PFN結構體資訊,進行初始化各項成員,引用計數設定為1。這樣就完成了一個物理頁面的申請。我們來分析MiRemoveZeroPage的實現,分析如何申請一個0化的頁面。分段閱讀

MmFreePagesByColor是一個PMMCOLOR_TABLES陣列,也就是一個二維陣列,定義如下

MmSecondaryColors和下面出現的MmSecondaryColorMask是輔助顏色值和輔助掩碼,MmSecondaryColors我個人理解為當前可用最大的顏色值。
FreePageList是一個上文提到的MMLIST列舉值,轉換為整數是1,所以陣列有2個元素,訪問時通過列舉值進行訪問。這個陣列第一個元素是零化的著色頁面,第二個元素是空閒著色的頁面。陣列的二維是一個由COLOR顏色值作下標的陣列,通過COLOR值就可以獲取到對應顏色的頁面連結串列,該連結串列的所有顏色就是這個COLOR。我們可以看到從0化頁面的對應顏色值取出了一個連結串列。

接下來判斷連結串列的頭個元素的值是否是LIST_HEAD,LIST_HEAD是巨集定義為-1,也就是這個連結串列中如果沒有值的話就要用-1表示。如果對應顏色的物理頁面連結串列為空,沒有元素的話,就應該從系統維護的零化頁面連結串列中取出一個新頁面,這裡通過PageIndex&MmSecondaryColorMask計算出新的COLOR值,可以看出COLOR值實際上是通過物理頁面下標來進行確定的。如果系統維護的零化頁面連結串列也為空,更新一下Zero值,表明等一下取出來的頁面應該手動去零化,然後從系統空閒頁面連結串列MmFreePageListHead取出頁面,同樣更新COLOR值,這時候如果MmFreePageListHead也為空,我們可以看到暫未實現。

根據頁面下標來取出PFN資料庫對應的PFN專案,進行檢查,如果需要零化就進行零化處理。我們分析MiRemovePageByColor的實現,分段閱讀

首先從頁面下標獲取對應MMPFN項,然後通過該項的MMPFN_ENTRY獲取得到頁面當前所處的系統維護的連結串列,這是通過PageLocation來獲得的,獲取到連結串列頭後,進行Total的減1,代表連結串列中元素個數減一

然後就是資料結構相關內容,從系統維護的連結串列中刪除該結點。

接下來取出MmFreePagesByColor對應的元素,也相應的將其刪除。更新計數。
我們還看到使用了MiDecrementAvailablePages函式,這個函式主要是遞減全域性變數MmAvailablePages,如果過低就判斷為系統可用記憶體過低觸發事件來修剪。
通過SouceInsight檢視MmAllocPage的函式為MmRequestPageMemoryConsumer,它實現了一個更通用的,可以為特定請求進行申請記憶體。

Consumer為巨集定義
Cache代表快取用,USER是使用者用,SYSTEM是系統使用,MAXIUM當前共有幾種用途,就是這3種用途
再看一個結構體

表明一個特定用途的消費者。系統將消費者分類存入了一個陣列

表明一共3種消費者

首先增加該消費者型別對應陣列的頁面使用數量,如果頁面數量使用次數過多,並且當前執行緒不是平衡用的執行緒,就要修剪騰出一些物理頁面

用MmAllocPage申請一個頁面返回 如果是使用者進行申請頁面MmInsertLRULastUserPage 設定使用者PFN點陣圖MiUserPfnBitMap,但是實際上在此處是使用者申請這個應該永遠不成立。前面已經過濾了只剩下系統的消費者了。


如果系統可用頁數已經小於最小可用頁數,那麼判斷呼叫者是否可用等待,如果不能等待,直接返回沒有記憶體錯誤,如果可以等待,就等待平衡執行緒去修剪記憶體騰出物理頁面,如果這時候還沒有頁碼,那麼就是嚴重錯誤了。這裡我們可以看到系統消費者和其他消費者的一個區別,如果系統消費者需要記憶體,就申請一個物理頁面,然後呼叫平衡執行緒去強制修剪其他的,但是如果是非系統消費者,如果不想等待就直接返回沒有記憶體,如果要等待就必須要等其他實體記憶體騰出,才能輪得到它申請到。

接下來是最後的申請頁面操作,前面有判斷的條件都不成立時,最終就執行到這裡。
接下來分析頁面釋放函式


函式很短,首先是判斷引用計數是否為1,如果是1代表是這個頁面目前只被引用一次,可以被掛入空閒連結串列
在這個函式中遞減了頁面引用計數,然後如果為0就會被掛入空閒連結串列之中。
讓我們再看一下這個函式

這個函式一般是這個頁面又被引用的時候進行使用,至此我們可以看到一個物理頁面的生命週期,首先是被申請,引用計數為1,接下來可能會被繼續引用(例如2個不同的虛擬地址頁面實際上指向一個物理頁面),當引用計數為0的時候,就會被掛入空閒連結串列MmFreePageListHead之中,並且也同樣掛入物理頁面對應的顏色連結串列MmFreePagesByColor中
本文由看雪論壇 黑色書卷 原創 轉載請註明來自看雪社群
關注看雪學院公眾號:ikanxue
更多幹貨等著你~
http://weixin.qq.com/r/M3W7oxbE-0uArVLA9yAh (二維碼自動識別)