你是否曾經遇到過在Linux系統中出現的各種記憶體問題?例如記憶體洩漏、記憶體碎片等等。這些問題都可以透過深入理解Linux kernel記憶體回收機制來解決。
無論電腦上有多少記憶體都是不夠的,因而linux kernel需要回收一些很少使用的記憶體頁面來保證系統持續有記憶體使用。頁面回收的方式有頁回寫、頁交換和頁丟棄三種方式:如果一個很少使用的頁的後備存儲器是一個塊設備(例如文件映射),則可以將內存直接同步到塊設備,騰出的頁面可以被重複使用;如果頁面沒有後備存儲器,則可以交換到特定swap分區,再次被訪問時再交換回內存;如果頁面的後備存儲器是一個文件,但文件內容在內存不能被修改(例如可執行文件),那麼在目前不需要的情況下可直接丟棄。
#2.1 頁框的回收
#
LRU(Least Recently Used),近期最少使用鍊錶,是按照近期的使用情況排列的,最少使用的存在鍊錶末尾,通過以下宏定義即可看出:
#define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru))
每個zone有5個LRU鍊錶用以存放各種最近使用狀態的頁面。
enum lru_list {
LRU_INACTIVE_ANON = LRU_BASE,
LRU_ACTIVE_ANON = LRU_BASE LRU_ACTIVE,
LRU_INACTIVE_FILE = LRU_BASE LRU_FILE,
LRU_ACTIVE_FILE = LRU_BASE LRU_FILE LRU_ACTIVE,
LRU_UNEVICTABLE,
NR_LRU_LISTS
};
其中INACTIVE_ANON、ACTIVE_ANON、INACTIVE_FILE、ACTIVE_FILE 4個鍊錶中的頁面是可以回收的。 ANON代表匿名映射,沒有後備記憶體;FILE代表檔案映射。
頁面回收時,會優先回收INACTIVE的頁面,只有當INACTIVE頁面很少時,才會考慮回收ACTIVE頁面。
為了評估頁的活動程度,kernel引入了PG_referend和PG_active兩個標誌位。為什麼需要兩個位呢?假定只使用一個PG_active來標識頁是否活動,在頁被存取時,設定該位,但是何時清楚呢?為此需要維護大量的核心定時器,這種方法注定是失敗的。
使用兩個標誌,可以實現一種更精巧的方法,其核心思想是:一個表示當前活動程度,一個表示最近是否被引用過,下圖說明了基本演算法。
#基本上有以下步驟:
(1)如果頁是活動的,設定PG_active位,並保存在ACTIVE LRU鍊錶;反之在INACTIVE;
(2)每次造訪頁時,設定PG_referenced位,負責該工作的是mark_page_accessed函數;
(3)PG_referenced以及由逆向映射提供的資訊用來確定頁面活動程度,每次清除該位元時,都會檢測頁面活動程度,page_referenced函數實現了該行為;
(4)再進入mark_page_accessed。如果發現PG_referenced已被置位,表示page_referenced沒有執行檢查,因而對於mark_page_accessed的呼叫比page_referenced更頻繁,這表示頁面經常被存取。如果該頁位於INACTIVE鍊錶,將其移至ACTIVE,此外還會設定PG_active標誌位,清除PG_referenced;
(5)反向的轉移也是有可能的,在頁面活動程度減少時,可能連續呼叫兩次page_referenced而中間沒有mark_page_accessed。
如果對記憶體頁的存取是穩定的,那麼對page_referenced和mark_page_accessed的呼叫本質上是均衡的,因而頁面保持在目前LRU鍊錶。這種方案同時確保了內存頁不會再ACTIVE與INACTIVE鍊錶間快速跳躍。
2.2 slab****快取回收
slab快取回收相對比較靈活,所有註冊到shrinker_list中的方法都會被執行。
核心預設針對每個檔案系統都註冊了prune_super方法,這個函數用來回收檔案系統中不再使用的dentry和inode快取;
android的lowmemorykiller機制註冊了選擇性殺死進程的方法,回收進程使用的記憶體。
3****怎麼回收頁框
其中shrink_page_list是真正回收頁面的過程
#4.1 kswapd
kswapd是核心為每個記憶體node創建的記憶體回收線程,為什麼有了緊缺回收機制還需要週期性回收呢?因為有些記憶體分配是不允許阻塞等待回收的,例如中斷和異常處理程序中的記憶體分配;還有些記憶體分配不允許啟動I/O存取的。只有少數情況的記憶體缺乏可以完整執行回收過程,所以利用系統空閒時間回收記憶體非常必要。
此函數記錄了上一次均衡操作時所使用的分配order,如果kswapd_max_order大於上一次的值,或classzone_idx小於上一次的值,則呼叫balance_pgdat再次均衡該記憶體域,否則可以進行短暫休眠,休眠的時間是HZ /10,對於arm(HZ=100)來說,休眠的時間就是1ms。
balance_pgdat均衡操作直到該記憶體域的zone_wartermark_ok為止。
4.2 cache_reap
cache_reap用來回收slab中的空閒對象,如果空閒對象可以還原成一個頁面,則釋放回buddy system。每次呼叫cache_reap會把所有的slab_caches遍歷一遍,之後休眠2*HZ,對於arm(HZ=100)來說,週期就是20ms。
總之,Linux kernel記憶體回收機制是一個非常重要的概念,可以幫助你更好地理解Linux系統中的記憶體管理。如果你想了解更多關於這個概念的信息,可以查看本文提供的參考資料。
#(1)《understanding the linux kernel》
(2)《professional linux kernel architecture》
以上是Linux kernel記憶體回收機制:深入理解記憶體管理的詳細內容。更多資訊請關注PHP中文網其他相關文章!