內存回收的關鍵函數以及其功能

zone_wartermark_ok()主要用來判斷當前zone的free pages個數是否大於參數的watermark的值,且當前zone存在連續的內存塊滿足內存分配時的2^order個頁數,order爲函數參數指定。

balance_pgdat():函數會先通過zone_balanced()從高到低來查找第一個不平衡的zone,然後判斷是否需要進行內存規整,最後zone號從低到高通過kswapd_shrink_zone()來進行內存回收。

zone_balanced():調用zone_watermark_ok()返回爲true表示平衡,否則不平衡,參數watermark爲high,order不爲零。

pgdat_needs_compaction:如果order大於0,則此變量爲true,zone_watermark_ok()返回爲true時(參數watermark爲low,order不爲0),此變量變爲false。

kswapd_shrink_zone(),首先設置需要回收的頁面數爲zone的high wartermark值,然後通過compaction_suitable()再次判斷當前zone是否需要進行內存規整,如果不需要且當前zone爲balanced,則直接return true。否則繼續條用shrink_zone,繼續進行內存回收。

compaction_suitable()函數主要返回三種參數:
1. 當zone_watermark_ok()返回爲true時(參數watermark爲low,order不爲0),則返回爲COMPACT_PARTIAL,表示有足夠內存滿足分配要求,不需要進行內存規整。

2. 當zone_watermark_ok()返回爲false時(參數watermark爲(low watermark+2^order),參數order爲0),返回COMPACT_SKIPPED,表示free pages太少,無法進行內存規整.

3. 當前zone的碎片率大於等於0小於等於500,返回COMPACT_SKIPPED,此時應該是表示不需要進行內存規整。

4. 除開以上幾種情況之後,返回COMPACT_CONTINUE,表示需要進行內存規整。

shrink_zone()函數沒什麼條件判斷,直接調用shrink_lruvec()函數進行內存回收。

shrink_lruvec()首先通過get_scan_count()得出每個lru上面掃描頁面的個數,分別存放在nr[NR_LRU_LISTS]數組中。然後通過while循環,在for循環中每次每個lru各掃描32個page,直到回收到的頁面數大於等於要回收的頁面數(kswapd_shrink_zone()中初始化的要回收的頁面數位high watermark頁面數)或者nr[NR_LRU_LISTS]數組inactive/active file lru和inactive anon lru變爲0,則退出while循環,然後將回收到的頁面數賦值給sc->nr_reclaimed。

5. shrink_list():如果是active lru,根據lru類型(file或者anon),判斷當前類型的active lru size是否大於inactive,如果大於,纔會shrink_active_list()來掃描對應類型的活躍lru,如果不是active lru,則直接調用shrink_inactive_lru()掃描inactive lru。

6. shrink_active_list():首先初始化三個臨時鏈表,調用isolate_lru_pages()掃描頁面,並分離出適合的頁面unmapped和clean的page,將分離出來的頁面放到臨時的l_hold鏈表中,然後掃描l_hold鏈表,將合適的頁面放回到對應類型的active和inactive鏈表中,這裏涉及到reclaim_stat中的recent_scanned和recent_rotated成員,recent_scanned指對應類型(只分爲file和anon兩種)每次掃描32個頁面後分離出來的符合條件的page數,recent_rotated值對應類型的返回active和inactive lru的頁面數之和。

shrink_active_list()中判斷頁面返回到適當lru的條件是:

  1. 當頁面時不可回收頁面時會返還到不可回收lru中。
  2. 通過page_referenced() RMAP機制判斷當前頁面是否被訪問或者引用的pte個數,如果大於0,則判斷當前是否爲可執行的file page,如果是則將page添加到active臨時鏈表,剩餘的頁面全部添加到對應類型的inactive臨時鏈表。
  3. 最後將active臨時鏈表和inactive臨時鏈表裏面的page全部添加到對應類型的lru中。

shrink_active_list()沒有返回值,表示此處沒有頁面被回收,只是完成頁面從active lru到inactive lru的轉化過程。

7. isolate_lru_pages():for循環從lru鏈表頭開始一頁一頁的判斷,調用__isolate_lru_page()判斷當前頁是否符合分離條件,如果符合就將page移到前面初始化了的l_hold鏈表上。

8. __isolate_lru_page():主要是page的判斷條件,判斷條件如下:

  1. 當前page不在lru上面的不能分離。
  2. page爲不可回收的頁面,但是isolate_mode不帶有分離不可回收頁面,則不能分離。
  3. 如果只分離乾淨的頁面,那麼正在回寫或者標記了PG_dirty的頁面不能分離。
  4. 如果分離unmaped page,但是當前頁面page->_mapcount>=0,則不分離頁面。
  5. 除去以上情況,其他頁面可以分離。

即分離出乾淨頁面和沒有映射的頁面。

9. shrink_inactive_list():首先調用isolate_lru_pages()分離出unmaped和clean的page到初始化的臨時鏈表page_list中,這個函數同樣會更新reclaim_stat中的recent_scanned和recent_rotated成員,將頁面分離到page_list之後會調用shrink_page_list()來完成內存回收。

10. shrink_page_list()判斷page_list裏面哪些頁面時可以回收的,具體條件整理如下:

  1. 如果PG_lock爲1,則表示當前頁面正在被操作,不能被回收,直接將page放回到ret_page list。
  2. 當前頁面的_mapcount大於等於0,當時回收條件不允許回收maped pages,則將page放到ret_page臨時鏈表。
  3. 如果page正在回寫,則將page放到ret_page臨時鏈表。
  4. 當page有訪問引用pte時,如果此頁爲anon,共享或者第二次訪問或者可執行的file page時會加入到active list,當有pte引用的其他情況下保留在inactive list。
  5. 最後剩下的沒有pte引用的page,函數接着往下執行。
  6. 經過上面的判斷,走到這裏只剩下沒有pte引用的page,如果是anon page,需要爲其創建swapcache分區,page mapping發生變化,需要重新賦值。
  7. 當前頁面爲dirty page,如果是file page,則讓其保留在inactive lru,設置可以回收標籤,在適當時候進行批量回收,如果爲anon page,則條用pageout()函數將page寫入交換區,根據pageout的返回值,可以對page有不同的操作(放回active lru,inactive lru或者可以釋放)。
  8. 最後處理page作爲buffer_head的情況。
  9. 剩餘情況表示可以回收的頁面,完成函數的執行,如果當前頁面可以回收,則回收頁面數加1。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章