内存回收的关键函数以及其功能

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。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章