• 你是什么内存: PageAnon 与 PageSwapBacked


    一、前言

        最近在翻阅内存回收相关代码,发现有 PageAnon(page) 和 PageSwapBacked(page) 两个函数/宏很费解。从一些资料上来看二者都有表示匿名页或者非文件页的含义,且内核中有二者的各种组合使用,这进一步加剧了我的迷惑:
        一个page究竟会在哪些场景下会出现如下情况:

    PageAnon(page) && PageSwapBacked(page)
    !PageAnon(page) && PageSwapBacked(page)
    PageAnon(page) && !PageSwapBacked(page)
    

        要了解他们的含义,先看看他们的实现细节, 二者在代码层面的实现如下:

    • PageAnon(page)
    #define PAGE_MAPPING_ANON       0x1
    static __always_inline int PageAnon(struct page *page)
    {
            page = compound_head(page);
            return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
    }
    

        即该函数判断page->mapping的bit0是否置1,如果是则PageAnon()返回true。

    • PageSwapBacked(page)

        而PageSwapBacked(page)则是判断page->flags是否有PG_swapbacked设置标志。

    二、不同的组合场景

    2.1 PageAnon(page) && PageSwapBacked(page)场景

        如果一个页PageAnon(page) && PageSwapBacked(page),则说明这个page是一个标准的匿名页,其page->mapping指向对应的vma->anon_vma。

        从下面的这个流程图可以看出端倪:

    图1 匿名页缺页异常分配page的流程

        上面的流程是缺页异常分配一个匿名页的流程,在这个流程中为新分配的page调用__SetPageSwapBacked()设置PG_swapbacked标志;
        然后调用WRITE_ONCE(page->mapping, (struct address_space *) anon_vma)设置page->mapping,这样设置后(page->mapping & PAGE_MAPPING_ANON) != 0,即PageAnon()为true。

    2.2 !PageAnon(page) && PageSwapBacked(page)

        这个场景是page为shmem的情况,这里为了与匿名页的情况进行对比,同样以缺页异常分配page流程来观察:

     图2 shmem缺页异常分配page流程

        这是shmem缺页异常的流程,这个流程先分配一个page,然后调用__SetPageSwapBacked()设置PG_swapbacked标志;
        但是其page->mapping指向的是struct adress_space*类型的inode->i_mapping,这是一个指针满足4字节(32位)或者8字节(64位)对齐,因而(page->mapping & PAGE_MAPPING_ANON)==0,即PageAnon()为false.

    2.3 PageAnon(page) && !PageSwapBacked(page)   

        这是一种比较特殊的情况,称为lazyfree pages,这种pages是通过madvise()对匿名页设置了MADV_FREE形成的。
        关于MADV_FREE相关的情况具体参考:https://man7.org/linux/man-pages/man2/madvise.2.html
        不过要形成PageAnon(page) && !PageSwapBacked(page)这种状态并非一蹴而就,而是分两个阶段:
          1) 将page加入到lazyfree缓存链表this_cpu_ptr(&lru_pvecs.lru_lazyfree)中,如下流程图所示:

    图3 madvise设置 MADV_FREE 页流程

          2) 清除PG_swapbacked标志

           清除lazyfree pages PG_swapbacked标志的点有两个:
           (1)lazyfree lru缓存链表满或者PageCompound(page)的情况
           (2)内存紧张进行回收时的情况
        这两种情况都会调用lru_lazyfree_fn()函数来将lazyfree lru缓存链表上的page转移到lru链表上。

     图4 将lazefree page转移到lru链表

    三、总结   

        除了上面的几个分析,实际上还可以参考include/linux/page-flags.h文件中对于PG_swapbacked标志的注释:

     * PG_swapbacked is set when a page uses swap as a backing storage.  This are
     * usually PageAnon or shmem pages but please note that even anonymous pages
     * might lose their PG_swapbacked flag when they simply can be dropped (e.g. as
     * a result of MADV_FREE).
    

        用个人粗糙的英文翻译一下大概就是:当一个page使用swap作为后备存储时需要为该page设置PG_swapbacked标志位。这种情况通常是匿名页或者shmem页,但是需要注意的是即使是匿名页也有可能不设置PG_swapbacked(例如MADV_FREE)。

  • 相关阅读:
    Swoole 协程使用示例及协程优先级
    Swoole 协程简介
    Laravel Redis分布式锁的使用
    Laravel Redis分布式锁实现源码分析
    Swoole 中使用异步任务
    runtime相关面试
    oc笔试题
    属性关键字面试题
    KVC面试题
    KVO面试题
  • 原文地址:https://www.cnblogs.com/liuhailong0112/p/14426096.html
Copyright © 2020-2023  润新知