Linux内存回收之LRU链表和第二次机会法

一 LRU回收算法

LRU算法,假定最近最少使用的页,在较短的时间内页不会使用,所以这些页成为回收的候选者.

内存回收的核心是围绕LRU链表来进行操作,Linux内核实现了5种LRU链表类型

  1. 不活跃匿名页表链表(LRU_INACTIVE_ANON)//shmem

  2. 活跃匿名页表链表(LRU_ACTIVE_ANON)//

  3. 不活跃文件映射页表链表(LRU_INACTIVE_FILE)

  4. 活跃文件映射页表链表(LRU_ACTIVE_FILE)

  5. 不可回收页表链表(LRU_UNEVICTABL)

内核宏定义:

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
};

LRU算法遵循先进先出(FIFO)原则

1.新分配的page链接到不活跃或活跃链表头部。

  1. 从活跃链表的尾部摘取页面链接到不活跃链表头部。

  2. 从不活跃链表尾部摘取页表进行内存回收.

在这里插入图片描述

每个zone都有一个lruvec结构体

struct lruvec {
struct list_head lists[NR_LRU_LISTS];//LRU链表
struct zone_reclaim_stat reclaim_stat;
};

整个内存回收,就是让页面在活跃链表/不活跃链表和Buddy system之间流动.

匿名页面回收时,如果没有使用者,页面直接被释放,如果有进程使用,先交换到swap分区,然后再释放,

文件映射页面只能回写磁盘,然后被释放.

二,第二次机会法

单纯基于LRU算法,INACTIVE LIST表头的page页面最容易被回收,这样可能存在频繁使用的page被换出去,为了防止这种情况,内核开发者提交了第二次机会法补丁,也就是标记页面是否频繁被引用/访问,如果频繁被使用,会多一次机会停留在不活跃链表,甚至还有机会迁移到活跃链表. 为了实现第二次机会法,内核定义了三个标志位

PG_active, PG_referenced,L_PTE_YOUNG,以及相关函数

mark_page_accessed: 标记页面访问

page_referenced: 获取page引用计数

page_check_references:跟page引用pte计数和访问计数,决定页面是否可被回收.

void mark_page_accessed(struct page *page)
{
if (!PageActive(page) && !PageUnevictable(page) &&
PageReferenced(page)) {
if (PageLRU(page))
activate_page(page);
else
__lru_cache_activate_page(page);
ClearPageReferenced(page);

} else if (!PageReferenced(page)) {
    SetPageReferenced(page);
}

}
规则如下:
inactive,unreferenced -> inactive,referenced
inactive,referenced -> active,unreferenced
active,unreferenced -> active,referenced

page_referenced(page)主要通过RMAP机制,计算引用了这个page,且最近被cpu访问过的pte数量(L_PTE_YOUNG置1),然后清除L_PTE_YOUNG,当pte再次被访问时,会产生缺页异常,handle_pte_fault调用pte_mkyoung再次标记L_PTE_YOUNG标志位

page_check_references:决定页面是否可以被回收.

调用关系:shrink_inactive_list->shrink_page_list->page_check_references:

static enum page_references page_check_references(struct page *page,
struct scan_control *sc)
{
int referenced_ptes, referenced_page;

/*计算最近被访问过pte数量 */
referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,
                  &vm_flags);

/*读取page引用标志位,并清楚标志位 */
referenced_page = TestClearPageReferenced(page);

/*如果有引用pte */
if (referenced_ptes) {
    /*匿名页面,重新链接到active list */
    if (PageSwapBacked(page))
        return PAGEREF_ACTIVATE;
    /*设置Referenced标志位 */
    SetPageReferenced(page);
    /*有引用标志位,或者共享的page cache,重新链接到active list */
    /*referenced_ptes > 1,这里可以过滤大量只读一次的文件页面迁移到active list  */
               if (referenced_page || referenced_ptes > 1)
        return PAGEREF_ACTIVATE;

    /*可执行文件页面,重新连接到active list  */
    if (vm_flags & VM_EXEC)
        return PAGEREF_ACTIVATE;
    /*如果只有引用pte,则继续保留在inactive list ,这就是所谓的第二次机会法??? */
    return PAGEREF_KEEP;
}
/*页面没有引用pte,则可以尝试回收 */
if (referenced_page && !PageSwapBacked(page))
    return PAGEREF_RECLAIM_CLEAN;
/*可回收最佳对象page */
return PAGEREF_RECLAIM;

}

page_check_references函数总结如下:

  1. 有引用pte时

1.1 匿名页面直接迁移回活跃链表

1.2 可执行文件页面,直接迁移回活跃链表

    1.3 共享page cache,以及最近第二次访问的page cache迁移回活跃链表

     1.4 只有引用pte,则保留在inactive list,且清PTE_YOUNG,设置PG_referenced

2.没有引用pte,则是回收的最佳候选者.

三, 第二次机会法举例

3.1 直接读取文件(read)

用户进程直接read文件时,内核调用了vfs_read,

第一次读: vfs_read->do_generic_file_read->page_cache_sync_readhead(预读文件)->read_pages(分配page)->add_to_page_cache_list(清PG_active,添加到inactive list),

                                 do_generic_file_read->mark_page_accessed(置标志位PG_referenced)

第一次read,返回后,page cache处于inactive list,且PG_referenced=1,PG_active=0,因为是直接读取,这个page没有对应的用户空间pte(也就是没有进行映射),如果此时页面回收对这个page调用page_check_references进行检查,会返回PAGEREF_RECLAIM_CLEAN

第二次读: vfs_read->do_generic_file_read->mark_page_accessed,因为PG_referenced=1,则标记PG_active,并添加到active list (这里体现第二次机会法)

3.2 mmap方式读取文件(ext4)

第一次读时,建立mmap映射->ext4_file_mmap->filemap_fault->ext4_readpages->add_to_page_cache_lru(清PG_active,并添加到inactive list )

第二次读写: 后面读取,就跟操作内存一样,

内存回收第一次对page调用page_check_references时,发现有引用pte,PG_referenced=0,则置PG_referenced=1(并清PTE_YOUNG标志),返回PAGEREF_KEEP,继续保留在不活跃链表(这里也体现第二次机会法).

内存回收第二次对page调用page_check_references时,

 1. 如果第一次调用page_check_references后,又通过pte访问了对应的page(L_PTE_YOUNG为1),则直接添加到ACTIVE LIST(这里体现了第二次机会法)

2. 如果第一次调用page_check_references后,没有在对page进行读取,则返回PAGEREF_RECLAIM_CLEAN

这里也可以看出mark_page_accessed函数是用于直接读取文件场景,而page_references用于mmap场景.

四 匿名/文件页面的产生

通过添加到LRU链表操作,可以知道哪些情况下会产生匿名页面和文件映射页面

4.1 匿名页面的产生

swap read和shmem通信时,产生不活跃匿名页面,调用lru_cache_add_anon添加到inactive lru

下面几种情况产生活跃页面,调用lru_cache_add_active_or_unevictable添加到active lru

do_wp_page:写时复制缺页异常
do_swap_page: KSM缺页异常

do_anonymous_page:malloc/mmap匿名缺页异常

4.2 文件映射页面产生

文件页面产生时,都是添加到不活跃LRU,主要通过文件直接read,和mmap操作产生page cache

add_to_page_cache_lru:文件系统预读时调用
lru_cache_add_file:cifs文件系统条用

五,page 的lru链表迁移

总的迁移图如下,

在这里插入图片描述

5.1 邻居子系统->活跃链表->临时链表->不活跃链表

产生逻辑: 用户空间malloc分配内存,产生匿名缺页中断(do_anonymous_page)进入邻居子系统分配内存(alloc_page)->添加到活跃链表(lru_cache_add_active_or_unevictable),当kswap线程扫描活跃链表时(shrink_active_list),会把页面从活跃链表分离到临时链表(isolate_lru_pages),然后把临时链表的page,迁移到不活跃链表.

5.2 邻居子系统->活跃链表->临时链表->邻居子系统

当活跃链表的page迁移到临时链表后,从临时链表迁移到不活跃链表会减少page引用计数,如果page计数为0,则直接释放到邻居子系统.

5.3 邻居子系统->不活跃链表->临时链表->活跃链表

用户空间mmap一个文件读操作时,产生缺页异常,从邻居子系统分配内存,然后添加到不活跃链表(add_to_page_cache_lru),

kswap线程扫描不活跃链表时(shrink_inactive_list),先分离页面到临时链表(isolate_lru_pages),然后通过page_check_references函数,判断页面是否要迁移到活跃链表(有pte引用计数,PG_refeferencs)

5.4 邻居子系统->不活跃链表->临时链表->swap/磁盘->邻居子系统

kswap线程扫描不活跃链表时(shrink_inactive_list),先分离页面到临时链表(isolate_lru_pages),然后通过page_check_references函数,判断页面是否能够被回收,如果能够被回收,先把page的数据回写writeback,或swap出去.然后再释放page到邻居子系统

6 页面回收条件

6.1 活跃链表迁移到不活跃链表

file list :当active file大于inactive file时,会把cache从活跃链表迁移到不活跃链表

ano list : 当inactive file *rate < active时,会把cache从活跃链表迁移到不活跃链表

根据是否满足迁移模式(dirty ,write back,mapcount)来分离页面. 然后迁移到不越活链表.

6.2 怎么确认回收哪个链表?

  1. 关闭swap时,只回收FILE

  2. swappiness为0,只回收FILE

  3. 不活跃文件页面大于活跃页面,只回收FILE

  4. zone free page + zone file 小于规定的高水位,则回收匿名链表

  5. 其他情况根据swappiness比例来回收.

6.3 什么时候退出回收

  1. 回收页面大于2<<order

  2. 回收线程应该从退出.

  3. 没有回收时.

6.4 怎么判读一个zone已经平衡

  1. 满足最高水位,且能分配order内存

  2. 当系统中的处于平衡状态的页面,大于 管理总页面数/2时,系统达到平衡

6.5 对zone每次回收多少个页面

 nr_to_reclaim = max(SWAP_CLUSTER_MAX, high_wmark_pages(zone))

6.6 扫描优先级

默认优先级为DEF_PRIORITY=12,每次回收失败时减1.

当每次scan的页面大于nr_to_reclaim时,不需要增加优先级.

6.7 vm_swappiness

【0,100】,越高,表示回收更多的匿名页面.
————————————————
版权声明:本文为CSDN博主「Robin.Yin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bin_linux96/article/details/84560607

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值