首发于术道经纬
Linux中的Anonymous Pages和Swap [一]

Linux中的Anonymous Pages和Swap [一]

进程的地址空间中,包括heap, stack和通过mmap匿名映射的区域,这些区域在建立的时候只有虚拟地址,当它们真正被访问到的时候,内核才会为其分配物理页面,这种方式被称作demand allocation(按需分配),它和page cache的demand paging的概念是对应的。这部分物理页面由于没有对应外部存储介质上的文件,因此被统称为anonymous pages

Anonymous pages同page cache一样,也是可以被回收的,但由于没有对应的后备文件,因此在回收anonymous pages的时候,不能像只读的page cache(比如text段)一样直接discard,或者像可读写的page cache(比如data段)一样flush同步后discard,而是需要保存这些anonymous pages的内容,这样才能在以后再次访问这些页面的时候,获得它们被回收前所包含的数据。

为此,磁盘上会开辟专门的swap space作为这些页面内容的backing store。swap space由若干的swap areas组成,swap area的最大数目由"MAX_SWAPFILES"确定(在4.19内核中这个宏的值为32)。

swap area可以有两种类型:swap partition和swap file,区别是swap partition在磁盘上是连续分布的,就像一个「裸」的磁盘设备一样,而swap file的内容则不一定连续分布,更像是基于磁盘设备的一个文件(参考这篇文章,需要注意的是,有时内核中会用"swapfile"这个术语统称这两者)。

Swap Area的创建

以生成swap file为例,首先使用"dd"命令创建一个空的文件,设置其路径名称(假设为"/swapfile1")和大小(假设为1GB)。

而后使用"mkswap"命令(源码位于util-linux-ng工具集中)在这个文件上创建swap area,最后使用"swapon"命令来初始化和激活这个swap area。

可通过"cat /proc/swaps"或者"swapon -s"命令获知系统包含的所有swap areas,在我的系统中,只有一个swap area,且其类型为swap partition:

如果有多个swap area,那么"Priority"高的swap area将被优先使用。

Swap Area的构成

这些swap areas分散在不同的块设备上,这样有助于实现硬件的并行访问。一个swap area在内核中由一个swap_info_struct结构体来描述和管理,整个swap space可以用swap_info[]数组表示。

/* The in-memory structure used to track swap areas. */
struct swap_info_struct {
    // 在swap_info[]中的索引编号
    signed char	type;  /* strange name for an index */
    // 数组指针,指向存储page引用计数的数组
    unsigned char *swap_map;  /* vmalloc'ed array of usage counts */
    // 所处的块设备
    struct block_device *bdev;	/* swap device or bdev of swap file */
    ...
 }

每个swap area又由若干的slots(也叫swap entry)组成,每个slot的大小和内存page的大小相同,其中第一个slot比较特殊,里面存放的是这个swap area的控制信息,用swap_header表示(类似于ELF header)。

swap_map作为指针,指向一个长度等于swap area中slots数目的数组,其主要作用将体现在下文将要介绍的swap in的过程中。

一个slot在磁盘上的位置,由它所在的swap area,以及它在这个swap area中的编号决定,两者合在一起就构成了swp_entry_t,其实就是一个整形数而已。

typedef struct {
     unsigned long val;
 } swp_entry_t;

内核使用了其中6个bits作为swap_info[]数组的索引,用于查找对应的swap area,索引值用swp_type()函数从swp_entry_t中提取。把索引命名为"type"也是挺奇怪的,这在上面介绍swap_info_struct结构体的"type"的注释中也能看出。

struct swap_info_struct *swap_info[MAX_SWAPFILES];
struct swap_info_struct *sis = swap_info[swp_type(swp_entry_t)];

24个bits作为swap area内的索引,用于查找对应的slot,索引值用swp_offset()函数从swp_entry_t中提取。

unsigned long offset = swp_offset(swp_entry_t);

Swap Out - 从内存到磁盘

内核在回收anonymous pages前,会把它们的内容复制到一个swap area的某个slot中保存起来,这个过程叫做swap out,对应的执行函数是add_to_swap()

首先需要调用get_swap_page()函数从swap area中分配空余的slot,然后增加swap cache(交换缓存)对准备swap out的页面的指向,并标记这个页面的状态为"dirty"。由于swap cache的作用主要体现在swap in的过程中,因此将放在下文详细介绍。

等到调用swap_writepage(),才会执行真正的I/O操作,将页面的内容写入外部的swap area,然后清除swap cache对页面的指向,释放页面所占的内存。

关于在此过程中vma的"vm_page_flags", PTE, page的"flags"的变化,请参考这张图片

从swap area中寻找空闲的slot的过程,其实和内存分配时寻找空闲的page很像。接下来将以SSD作为外部存储介质为例,讲解分配swap slot的具体方法。

【swap cluster

对于传统的磁盘设备,最好将访问的数据连续分布,以加快读写速度,减少磁盘碎片。对于特性与磁盘完全不同的SSD来说,随机读写的速度也是很快的,把数据集中放在物理相邻的位置不仅没必要,反倒对SSD需要的损耗均衡(wear leveling)是不利的。

为此,在内核3.12版本上加入了一项特性,当swap子系统知道它正在使用SSD时,它会将设备划分为clusters,每个cluster包括"SWAPFILE_CLUSTER"个pages(4.19内核中这个宏的值是256)。

"swap_info_struct"结构体中和cluster相关的域如下(各项的具体含义将在后面的描述中逐渐解释):

/* Only for SSD */
struct swap_cluster_list free_clusters; /* free clusters list */
unsigned int cluster_next;	/* likely index for next allocation */
unsigned int cluster_nr;	/* countdown to next cluster search */

每个CPU有对应的cluster,由swap_cluster_info结构体管理。

struct swap_cluster_info {
    spinlock_t lock;	
    unsigned int data:24;
    unsigned int flags:8;
};

CPU会从自己的cluster上分配slot,这样既可以减少CPU之间的竞争,增加swap out时系统总的througout,也可以让数据在SSD上的分布更加分散,更详细的介绍请参考这篇文章

点击图片可获得更清晰视图

伴随使用过程中slot的释放,cluster中会出现若干空闲的slots,但如果分配时从这些空闲的slots中寻找,效率较低,因此采用的是从cluster中未分配的slots查找(未分配的slots数目由"cluster_nr"表示)。

当一个cluster耗尽后("cluster_nr"的值为0),可调用swap_alloc_cluster()函数从"free_clusters"中获取一个新的cluster("cluster_next"是下一个要使用的cluster的第一个slot的编号)。如果已经没有新的cluster,再从既有的clusters中进行更细粒度的空闲slots查找。

当一个CPU的cluster用完时,也可能会使用其他CPU的cluster,这就是为什么"swap_cluster_info"中存在spinlock的原因。为了加快分配速度,可以采用swap slot cache(注意区别于swap cache)。

在swap area找到空闲的slot后,swp_entry_t就确定下来了,那这个swp_entry_t的信息保存在什么地方呢,请看下文分解。


参考:

jake.dothome.co.kr/swap


原创文章,转载请注明出处。

编辑于 2020-03-27 10:55