linux内存回收机制
無論計(jì)算機(jī)上有多少內(nèi)存都是不夠的,因而linux kernel需要回收一些很少使用的內(nèi)存頁(yè)面來保證系統(tǒng)持續(xù)有內(nèi)存使用。頁(yè)面回收的方式有頁(yè)回寫、頁(yè)交換和頁(yè)丟棄三種方式:如果一個(gè)很少使用的頁(yè)的后備存儲(chǔ)器是一個(gè)塊設(shè)備(例如文件映射),則可以將內(nèi)存直接同步到塊設(shè)備,騰出的頁(yè)面可以被重用;如果頁(yè)面沒有后備存儲(chǔ)器,則可以交換到特定swap分區(qū),再次被訪問時(shí)再交換回內(nèi)存;如果頁(yè)面的后備存儲(chǔ)器是一個(gè)文件,但文件內(nèi)容在內(nèi)存不能被修改(例如可執(zhí)行文件),那么在當(dāng)前不需要的情況下可直接丟棄。
?
1 回收的時(shí)機(jī)
2 哪些內(nèi)存可以回收
2.1 頁(yè)框的回收
LRU(Least Recently Used),近期最少使用鏈表,是按照近期的使用情況排列的,最少使用的存在鏈表末尾,通過以下宏定義即可看出:
#define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru))
每個(gè)zone有5個(gè)LRU鏈表用以存放各種最近使用狀態(tài)的頁(yè)面。
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個(gè)鏈表中的頁(yè)面是可以回收的。ANON代表匿名映射,沒有后備存儲(chǔ)器;FILE代表文件映射。
頁(yè)面回收時(shí),會(huì)優(yōu)先回收INACTIVE的頁(yè)面,只有當(dāng)INACTIVE頁(yè)面很少時(shí),才會(huì)考慮回收ACTIVE頁(yè)面。
為了評(píng)估頁(yè)的活動(dòng)程度,kernel引入了PG_referend和PG_active兩個(gè)標(biāo)志位。為什么需要兩個(gè)位呢?假定只使用一個(gè)PG_active來標(biāo)識(shí)頁(yè)是否活動(dòng),在頁(yè)被訪問時(shí),設(shè)置該位,但是何時(shí)清楚呢?為此需要維護(hù)大量的內(nèi)核定時(shí)器,這種方法注定是要失敗的。
使用兩個(gè)標(biāo)志,可以實(shí)現(xiàn)一種更精巧的方法,其核心思想是:一個(gè)表示當(dāng)前活動(dòng)程度,一個(gè)表示最近是否被引用過,下圖說明了基本算法。
?
基本上有以下步驟:
(1)如果頁(yè)是活動(dòng)的,設(shè)置PG_active位,并保存在ACTIVE LRU鏈表;反之在INACTIVE;
(2)每次訪問頁(yè)時(shí),設(shè)置PG_referenced位,負(fù)責(zé)該工作的是mark_page_accessed函數(shù);
(3)PG_referenced以及由逆向映射提供的信息用來確定頁(yè)面活動(dòng)程度,每次清除該位時(shí),都會(huì)檢測(cè)頁(yè)面活動(dòng)程度,page_referenced函數(shù)實(shí)現(xiàn)了該行為;
(4)再次進(jìn)入mark_page_accessed。如果發(fā)現(xiàn)PG_referenced已被置位,意味著page_referenced沒有執(zhí)行檢查,因而對(duì)于mark_page_accessed的調(diào)用比page_referenced更頻繁,這意味著頁(yè)面經(jīng)常被訪問。如果該頁(yè)位于INACTIVE鏈表,將其移動(dòng)到ACTIVE,此外還會(huì)設(shè)置PG_active標(biāo)志位,清除PG_referenced;
(5)反向的轉(zhuǎn)移也是有可能的,在頁(yè)面活動(dòng)程度減少時(shí),可能連續(xù)調(diào)用兩次page_referenced而中間沒有mark_page_accessed。
如果對(duì)內(nèi)存頁(yè)的訪問是穩(wěn)定的,那么對(duì)page_referenced和mark_page_accessed的調(diào)用在本質(zhì)上是均衡的,因而頁(yè)面保持在當(dāng)前LRU鏈表。這種方案同時(shí)確保了內(nèi)存頁(yè)不會(huì)再ACTIVE與INACTIVE鏈表間快速跳躍。
2.2 slab緩存回收
slab緩存回收相對(duì)比較靈活,所有注冊(cè)到shrinker_list中的方法都會(huì)被執(zhí)行。
內(nèi)核默認(rèn)針對(duì)每個(gè)文件系統(tǒng)都注冊(cè)了prune_super方法,這個(gè)函數(shù)用來回收文件系統(tǒng)中不再使用的dentry和inode緩存;
android的lowmemorykiller機(jī)制注冊(cè)了選擇性殺死進(jìn)程的方法,回收進(jìn)程使用的內(nèi)存。
3怎樣回收頁(yè)框
其中shrink_page_list是真正回收頁(yè)面的過程
?
4周期性回收的頻率
4.1 kswapd
kswapd是內(nèi)核為每個(gè)內(nèi)存node創(chuàng)建的內(nèi)存回收線程,為什么有了緊缺回收機(jī)制還需要周期性回收呢?因?yàn)橛行﹥?nèi)存分配是不允許阻塞等待回收的,比如中斷和異常處理程序中的內(nèi)存分配;還有些內(nèi)存分配不允許激活I(lǐng)/O訪問的。只有少數(shù)情況的內(nèi)存緊缺可以完整執(zhí)行回收過程,所以利用系統(tǒng)空閑時(shí)間回收內(nèi)存非常必要。
該函數(shù)記錄了上一次均衡操作時(shí)所用的分配order,如果kswapd_max_order大于上一次的值,或者classzone_idx小于上一次的值,則調(diào)用balance_pgdat再次均衡該內(nèi)存域,否則可以進(jìn)行短暫休眠,休眠的時(shí)間是HZ/10,對(duì)于arm(HZ=100)來說,休眠的時(shí)間就是1ms。
balance_pgdat均衡操作直到該內(nèi)存域的zone_wartermark_ok為止。
4.2 cache_reap
cache_reap用來回收slab中的空閑對(duì)象,如果空閑對(duì)象可以還原成一個(gè)頁(yè)面,則釋放回buddy system。每次調(diào)用cache_reap會(huì)把所有的slab_caches遍歷一遍,之后休眠2*HZ,對(duì)于arm(HZ=100)來說,周期就是20ms。
5 參考文獻(xiàn)
(1)《understanding the linux kernel》
(2)《professional linux kernel architecture》
總結(jié)
以上是生活随笔為你收集整理的linux内存回收机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QT中VideoProbe的简介和实现
- 下一篇: 移动工具V和选区工具M