1. 程式人生 > >linux內存回收機制

linux內存回收機制

阻塞 減少 檢測 當前 情況 時機 為什麽 log arm

無論計算機上有多少內存都是不夠的,因而linux kernel需要回收一些很少使用的內存頁面來保證系統持續有內存使用。頁面回收的方式有頁回寫、頁交換和頁丟棄三種方式:如果一個很少使用的頁的後備存儲器是一個塊設備(例如文件映射),則可以將內存直接同步到塊設備,騰出的頁面可以被重用;如果頁面沒有後備存儲器,則可以交換到特定swap分區,再次被訪問時再交換回內存;如果頁面的後備存儲器是一個文件,但文件內容在內存不能被修改(例如可執行文件),那麽在當前不需要的情況下可直接丟棄。

1 回收的時機

2 哪些內存可以回收

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周期性回收的頻率

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。

5 參考文獻

(1)《understanding the linux kernel》

(2)《professional linux kernel architecture》

linux內存回收機制