實際上,在用完所有空閒內存之前,就必須執行頁框回收算法。否則,內核很可能陷入一種內存請求的僵局中,並導致系統崩潰。也就是說,要釋放一個頁框,內核就必須把頁框的數據寫入磁盤;但是,為了完成這一操作,內核卻要請求另一個頁框(例如,為I/O數據傳送分配緩沖區首部)。因為不存在空閒頁框,因此,不可能釋放頁框。
頁框算法的目標之一就是保存最少的空閒頁框並使內核可以安全地從“內存緊缺”的情形中恢復過來。
選擇目標頁
PFRA的目標就是獲得頁框並使之空閒。PFRA按照頁框所含內容,以不同的方式處理頁框。我們將他們區分成:不可回收頁、可交換頁、可同步頁和可丟棄頁:
頁類型
說明
回收操作
不可回收頁
空閒頁(包含子伙伴系統列表中)
保留頁(PG_reserved標志置位)
內核動態分配頁
進程內核態堆棧頁
臨時鎖定頁(PG_locked標志置位)
內存鎖定頁(在先行區中且VM_LOCKED標志置位)
不允許也無需回收
可回收頁
用戶太地址空間的匿名頁
Tmpfs文件系統的映射頁(如IPC共享內存的頁)
將頁的內容保存在交換區
可同步頁
用戶態地址空間的映射頁
存有磁盤文件數據且在頁高速緩存中的頁
塊設備緩沖區頁
某些磁盤高速緩存的頁(如索引節點高速緩存)
必要時,與磁盤鏡像同步這些頁
可丟棄頁
內存高速緩存中的未使用頁(如slab分配器高速緩存)
目錄想高速緩存的未使用頁
無需操作
進行頁面回收的時機
Linux 操作系統使用如下這兩種機制檢查系統內存的使用情況,從而確定可用的內存是否太少從而需要進行頁面回收。
如果操作系統在進行了內存回收操作之後仍然無法回收到足夠多的頁面以滿足上述內存要求,那麼操作系統只有最後一個選擇,那就是使用 OOM( out of memory )killer,它從系統中挑選一個最合適的進程殺死它,並釋放該進程所占用的所有頁面。
上面介紹的內存回收機制主要依賴於三個字段:pages_min,pages_low 以及 pages_high。每個內存區域( zone )都在其區域描述符中定義了這樣三個字段,這三個字段的具體含義如下表 所示。
字段含義
名稱
字段描述
pages_min
區域的預留頁面數目,如果空閒物理頁面的數目低於 pages_min,那麼系統的壓力會比較大,此時,內存區域中急需空閒的物理頁面,頁面回收的需求非常緊迫。
pages_low
控制進行頁面回收的最小阈值,如果空閒物理頁面的數目低於 pages_low,那麼操作系統內核會開始進行頁面回收。
pages_high
控制進行頁面回收的最大阈值,如果空閒物理頁面的數目多於 pages_high,則內存區域的狀態是理想的。
PFRA設計
設計總則
1. 首先釋放“無害”頁,即必須線回收沒有被任何進程使用的磁盤與內存高速緩存中的頁;
2. 將用戶態進程和所有頁定為可回首頁,FPRA必須能夠竊得人任何用戶態進程頁,包括匿名頁。這樣,睡眠較長時間的進程將逐漸失去所有頁;
3. 同時取消引用一個共享頁的所有頁表項的映射,就可以回收該共享頁;
4. 只回收“未用”頁,使用LRU算法。Linux使用每個頁表項中的訪問標志位,在頁被訪問時,該標志位由銀獎自動置位;而且,頁年齡由頁描述符在鏈表(兩個不同的鏈表之一)中的位置來表示。
因此,頁框回收算法是集中啟發式方法的混合:
1. 謹慎選擇檢查高速緩存的順序;
2. 基於頁年齡的變化排序;
3. 區別對待不同狀態的頁;
反向映射
PFRA的目標之一是能釋放共享頁框。為達到這個目地。Linux內核能夠快速定為指向同一頁框的所有頁表項。這個過程就叫做反向映射。Linux 操作系統為物理頁面建立一個鏈表,用於指向引用了該物理頁面的所有頁表項。
基本思想如下圖:
Linux采用“面向對象的反向映射”技術。實際上,對任何可回收的用戶態頁,內核保留系統中該頁所在所有現行區(“對象”)的反向鏈接,每個線性區描述符( vm_area_struct 結構)存放一個指針指向一個內存描述符( mm_struct 結構),而該內存描述符又包含一個指針指向一個頁全局目錄(PGD)。因此,這些反向鏈接使得PFRA能夠檢索引用某頁的所有頁表項。因為線性區描述符比頁描述符少,所以更新共享頁的反向鏈接就比較省時間。下面是具體的實現: