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链接到不活跃或活跃链表头部。

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

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

每个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

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

   4. 其他情况根据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】,越高,表示回收更多的匿名页面.

 

   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值