一、前言
最近在翻阅内存回收相关代码,发现有 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)。