您好,登錄后才能下訂單哦!
1 回收哪些頁面
Page cache;
用戶地址空間的內存映射頁面;
Slab緩存:如dentry和inode cache;
匿名頁:進程堆棧和mmap匿名映射內存區;回收前先換置到swap;
2 何時回收
Kswapd定期喚醒:當系統空閑內存小于閾值則進行頁面回收;
直接頁面回收:假設操作系統需要通過伙伴系統為用戶進程分配一大塊內存,或者需要創建一個很大的緩沖區,而當時系統中的內存沒有辦法提供足夠多的物理內存以滿足這種內存請求,這時候,操作系統就必須盡快進行頁面回收操作;
OS嘗試內存回收后仍無法獲取足夠頁面,則調用find_bad_process并進行OOM kill;
3 如何回收
基于LRU算法;每個zone維護兩個LRU鏈表
struct zone {
……
spinlock_t lru_lock;
struct list_head active_list;
struct list_head inactive_list;
unsigned long nr_active;
unsigned long nr_inactive;
……
}
PG_active/PG_referenced用于標識頁面活躍度,前者標識頁面時活躍的;后者表示頁面最近是否被訪問過,每訪問一次便會置位;
注:假如只是用一個標志符,在頁面被訪問時,置位該標志符,之后該頁面一直處于活躍狀態,如果操作系統不清除該標志位,那么即使之后很長一段時間內該頁面都沒有或很少被訪問過,該頁面也還是處于活躍狀態。為了能夠有效清除該標志位,需要有定時器的支持以便于在超時時間之后該標志位可以自動被清除。然而,很多 Linux 支持的體系結構并不能提供這樣的硬件支持,所以 Linux 中使用兩個標志符來判斷頁面的活躍程度。
Linux依據這兩個字段將page在active_list和inactive_list之間移動;
注:1 表示函數 mark_page_accessed(),2 表示函數 page_referenced(),3 表示函數 activate_page(),4 表示函數 shrink_active_list()
不管是kswapd還是直接頁面回收,最終都調用shrink_slab和shrink_zone;
直接頁面回收:反復調用這兩個函數,若特定循環次數內沒能成功釋放N個page,則調用OOM killer;
Kswapd:對每個zone都調用shrink_zone();
3.1 Shrink_slab原理
先向操作系統內核注冊 shrinker函數,會在內存較少的時候主動釋放一些該磁盤緩存占用的空間。
函數 shrink_slab() 會遍歷 shrinker 鏈表,從而對所有注冊了 shrinker 函數的磁盤緩存進行處理。
注冊 shrinker 是通過函數 set_shrinker() 實現的,解除 shrinker 注冊是通過函數 remove_shrinker() 實現的。當前,Linux 操作系統中主要的 shrinker 函數有如下幾種:
shrink_dcache_memory():該 shrinker 函數負責 dentry 緩存。
shrink_icache_memory():該 shrinker 函數負責 inode 緩存。
mb_cache_shrink_fn():該 shrinker 函數負責用于文件系統元數據的緩存。
3.2 Shrink_zone原理
1 通過shrink_active_list()將頁面從active移到inactive list;
2 調用shrink_inactive_list()將inactive
list的頁放入臨時鏈表,最終調用shrink_page_list()回收
3.2.1 Swappiness的意義
上文提到的shrink_zone() 會調用 shrink_lruvec(),而active/inactive list又各分為anon匿名頁和file cache映射頁鏈表,總計4個LRU;
而swappines只針對anon page,即便其為0也有可能執行swap。
vmscan.c中的get_scan_coun()
1. 首先如果系統禁用了swap或者沒有swap空間,則只掃描file based的鏈表,即不進行匿名頁鏈表掃描
代碼:
if (!sc->may_swap || (get_nr_swap_pages() <= 0)) {
scan_balance = SCAN_FILE;
goto out;
}
2. 如果當前進行的不是全局頁回收(cgroup資源限額引起的頁回收),并且swappiness設為0,則不進行匿名頁鏈表掃描,
代碼:
if (!global_reclaim(sc) && !vmscan_swappiness(sc)) {
scan_balance = SCAN_FILE;
goto out;
}
3. 如果進行鏈表掃描前設置的priority(這個值決定掃描多少分之一的鏈表元素)為0,且swappiness非0,則可能會進行swap
代碼:
if (!sc->priority && vmscan_swappiness(sc)) {
scan_balance = SCAN_EQUAL;
goto out;
}
4. 如果是全局頁回收,并且當前空閑內存和所有file based鏈表page數目的加和都小于系統的high watermark,則必須進行匿名頁回收,則必然會發生swap
代碼:
anon = get_lru_size(lruvec, LRU_ACTIVE_ANON) +
get_lru_size(lruvec, LRU_INACTIVE_ANON);
file = get_lru_size(lruvec, LRU_ACTIVE_FILE) +
get_lru_size(lruvec, LRU_INACTIVE_FILE);
if (global_reclaim(sc)) {
free = zone_page_state(zone, NR_FREE_PAGES);
if (unlikely(file + free <= high_wmark_pages(zone))) {
scan_balance = SCAN_ANON;
goto out;
}
}
5. 如果系統inactive file鏈表比較充足,則不考慮進行匿名頁的回收,即不進行swap
代碼:
if (!inactive_file_is_low(lruvec)) {
scan_balance = SCAN_FILE;
goto out;
}
注:每個zone有min/low/high 3個值,而high watermark指的是最后一個,這3個值依據vm.min_free_kbytes設置
3.2.2 反向映射
回收物理頁前需要解除所有關聯該頁的頁表項,而共享內存中的頁可能被多個進程引用,因此需要一種機制快速定位頁表項;
2.4要遍歷所有進程;
2.5引入反向映射,每個物理頁維護一個頁表項鏈表;
2.6引入基于對象的反向映射,每個物理頁設置一個反向映射鏈表,鏈表節點為vm_area_struct結構,其通過mm_struct找到pgd進而找到相應頁表項;
struct page {
atomic_t _mapcount; --初始值是 -1,每增加一個使用者,該計數器加 1
union {
……
struct {
……
struct address_space *mapping; --如果最低位置位,為指向 anon_vma 結構(用于匿名頁面)的指針;否則為 address_space 指針(用于基于文件映射的頁面)。
};
……
};
對于匿名頁面來說,頁面雖然可以是共享的,但是一般情況下,共享匿名頁面的使用者的數目不會很多;而對于基于文件映射的頁面來說,共享頁面的使用者的數目可能會非常多,使用優先級搜索樹這種結構可以更加快速地定位那些引用了該頁面的虛擬內存區域。操作系統會為每一個文件都建立一個優先級搜索樹,其根節點可以通過結構 address_space 中的 i_mmap 字段獲取。
注:LRU緩存
頁面根據其活躍程度會在 active 鏈表和 inactive 鏈表之間來回移動,如果要將某個頁面插入到這兩個鏈表中去,必須要通過自旋鎖以保證對鏈表的并發訪問操作不會出錯。為了降低鎖的競爭,Linux 提供了一種特殊的緩存:LRU 緩存,用以批量地向 LRU 鏈表中快速地添加頁面。有了 LRU 緩存之后,新頁不會被馬上添加到相應的鏈表上去,而是先被放到一個緩沖區中去,當該緩沖區緩存了足夠多的頁面之后,緩沖區中的頁面才會被一次性地全部添加到相應的 LRU 鏈表中去。
LRU 緩存用到了 pagevec 結構,如下所示 :
struct pagevec {
unsigned long nr;
unsigned long cold;
struct page *pages[PAGEVEC_SIZE];
};
lru_cache_add() 和 lru_cache_add_active()。前者用于延遲將頁面添加到 inactive 鏈表上去,后者用于延遲將頁面添加到 active 鏈表上去。這兩個函數都會將要移動的頁面先放到頁向量 pagevec 中,當 pagevec 滿了(已經裝了 14 個頁面的描述符指針),pagevec 結構中的所有頁面才會被一次性地移動到相應的鏈表上去。
參考資料
http://www.ibm.com/developerworks/cn/linux/l-cn-pagerecycle/index.html?ca=dat
http://www.douban.com/note/349467816/
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。