一、页面回收概述
Linux中页面回收主要是通过两种方式触发的,一种是由“内存严重不足”事件触发的;一种是由后台进程 kswapd触发的,该进程周期性地运行,一旦检测到内存不足,就会触发页面回收操作。对于第一种情况,系统会调用函数 try_to_free_pages()去检查当前内存区域中的页面,回收那些最不常用的页面。对于第二种情况,函数 balance_pgdat()是入口函数。以下是linux3.5中页面回收关键代码流程图。
其中,shrink_zone() 函数是 Linux操作系统实现页面回收的最核心的函数之一,它实现了对一个内存区域的页面进行回收的功能,该函数主要做了两件事情:
- 将某些页面从 active 链表移到 inactive 链表,这是由函数 shrink_active_list() 实现的。
- 从 inactive 链表中选定一定数目的页面,将其放到一个临时链表中,这由函数 shrink_inactive_list() 完成。该函数最终会调用 shrink_page_list() 去回收这些页面。函数 shrink_page_list() 返回的是回收成功的页面数目。
二、LRU链表
LRU链表是页框回收算法的核心数据结构。lru链表是统称,细分为:活动链表、非活动链表。
每个zone都有一组lru链表,页面都被放到自己对应的zone的LRU中。
structzone {
……
struct lruvec lruvec;
……
}
一组LRU由几对链表组成,有磁盘高速缓存页面(包括文件映射页面)的链表、匿名映射页面的链表、等。一对链表实际上是active和inactive两个链表,前者是最近使用过的页面、后者是最近未使用的页面。
struct lruvec {
structlist_head lists[NR_LRU_LISTS];
structzone_reclaim_stat reclaim_stat;
#ifdefCONFIG_CGROUP_MEM_RES_CTLR
structzone *zone;
#endif
};
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
};
处于此两个链表中的页框标志位也需要相应设置:PG_lru,PG_active。前者的标志表明页在活动或非活动页链表中,而后者是页在活动链表中。当是属于非活动链表时,标志位要清。