什么是 zSwap

zSwap 是 Linux 内核的一个功能,它为交换页提供了一个压缩的回写缓存,作为一种虚拟内存压缩形式。当内存页要被换出时,zSwap 不会把它们移到交换设备上,而是对它们进行压缩,然后把它们存储到系统 RAM 中动态分配的内存池中。后来,向实际的交换设备的回写被推迟了,甚至完全避免了,从而大大减少了需要交换的 Linux 系统的 I/O,其代价是需要额外的 CPU 周期来执行压缩。

zSwap 允许 Linux 更有效地利用 RAM,因为它实际上增加了内存容量,而不是在压缩/解压缩交换页时稍微增加 CPU 的使用。与 zram 相比的区别在于,zswapswap 设备协同工作,而 zram 是内存中的交换设备,不需要后备交换设备。zSwap 存在于内核中,但默认并没有开启,要使用它必须通过修改配置文件开启。

主流内存压缩技术

  • zSwap

​zSwap 是在 memory 与 flash 之间的一层缓存,当内存需要 swap 出去磁盘的时候,先通过压缩放到 zSwap 中去,zSwap 空间按需增长。达到一定程度后则会按照 LRU 的顺序(前提是使用的内存分配方法需要支持 LRU)将就最旧的 page 解压写入磁盘 swap device,之后将当前的 page 压缩写入 zSwap

zSwap 本身存在一些缺陷或问题:如果开启当 zSwap 满交换出 backing store 的功能, 由于需要将 zSwap 里的内存按 LRU 顺序解压再 swap out,这就要求内存分配器支持 LRU 功能;如果不开启当 zSwap 满交换出 backing store 的功能, 和 zRam 是类似的。

  • zRAM

zRram 即压缩的内存,使用内存模拟 block device 的做法。实际不会写到块设备中去,只会压缩后写到模拟的块设备中,其实也就是还是在 RAM 中,只是通过压缩了。由于压缩和解压缩的速度远比读写 I/O 好,因此在移动终端设备广泛被应用。

zRram 本身存在一些缺陷或问题:zRam 大小是可灵活配置的,配置多少成为了一个问题;使用 zRam 可能会在低内存场景由于频繁的内存压缩导致 kswapd 进程占 CPU 高;增大了 zRam 配置,对系统内存碎片是否有影响

  • zCache

​zCache 是 oracle 提出的一种实现文件页压缩技术,也是 memory 与 block dev 之间的一层存储,与 zSwap 比较接近,但 zCache 目前压缩的是文件页,而 zSwapzRAM 压缩是匿名页。

开启 zSwap

使用内核参数开启

root 身份编辑 grub 配置文件

1
sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT 行加入 zswap.enabled=1 zswap.compressor=zstd zswap.zpool=z3fold,完成后如下

1
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash zswap.enabled=1 zswap.compressor=zstd zswap.zpool=z3fold"

更新 grub 配置

1
sudo update-grub

重启即可

通过 sysfs 接口进行更改

由于 zSwap 配置可以在运行时通过 sysfs 接口进行更改,在上述方法无效时,可以通过 service 的方式在开机时自动启动 zSwap

创建 /etc/systemd/system/zswap.service

1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Enable zSwap
After=multi-user.target

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/init-zswap

[Install]
WantedBy=multi-user.target

创建 /usr/bin/init-zswap

1
2
touch /usr/bin/init-zswap
chmod +x /usr/bin/init-zswap

编辑 /usr/bin/init-zswap

1
2
3
4
5
#!/bin/sh

echo zstd > /sys/module/zswap/parameters/compressor
echo z3fold > /sys/module/zswap/parameters/zpool
echo 1 > /sys/module/zswap/parameters/enabled

完成后 sudo systemctl enable zswap,重启即可

配置 zSwap 参数

这部分内容来自 zswap - Arch Linux中文维基

当前参数

zswap 有几个可自定义的参数。可以使用以下方式显示实时设置:

1
$ grep -R . /sys/module/zswap/parameters
1
2
3
4
5
6
/sys/module/zswap/parameters/same_filled_pages_enabled:Y
/sys/module/zswap/parameters/enabled:Y
/sys/module/zswap/parameters/max_pool_percent:20
/sys/module/zswap/parameters/compressor:lz4
/sys/module/zswap/parameters/zpool:z3fold
/sys/module/zswap/parameters/accept_threshold_percent:90

zswap 文档 获取不同参数的描述。

显示初始配置的引导时加载消息可以通过以下方式检索:

1
dmesg | grep zswap:
1
[    0.317569] zswap: loaded using pool lz4/z3fold

设定参数

使用 sysfs

每个设置都可以在运行时通过 sysfs 接口进行更改。作为示例,要更改 compressor 参数:

1
echo lz4 > /sys/module/zswap/parameters/compressor

使用内核引导参数

要持久化参数更改,必须在内核引导参数中添加相应的选项,例如 zswap.compressor=lz4。因此,要永久设定上述所有设置,必须添加以下内核参数:

1
zswap.enabled=1 zswap.compressor=lz4 zswap.max_pool_percent=20 zswap.zpool=z3fold

当通过引导参数更改压缩算法时,需要确保在引导期间尽早加载相应的压缩模块(参考 #压缩算法)。

最大池大小

存储池不是预先分配的,它可以增长到可用内存总量的一定百分比,默认情况下最多占内存总量的20%。一旦达到此阈值,就会将页从池中逐出到交换设备中。压缩池的最大大小由参数 max_pool_percent 控制。

压缩存储池分配器

zpool 参数控制压缩存储池的管理。

使用 zbud 数据分配器,2个压缩对象被存储到1个页中,这将压缩比限制为2或更小。

更好的 z3fold 分配器允许每页最多3个压缩对象。z3fold 的压缩比通常为2.7,而 zbud 的压缩比通常为1.7。

默认情况下,会创建 z3fold 类型的 zpool。使用内核参数 zswap.zpool 在启动时选择另一种方法。数据分配器也可以在稍后阶段通过 sysfs 接口进行更改。

压缩算法

对于页的压缩,zswap 使用内核加密 API 提供的压缩器模块。默认情况下会使用 lz4 压缩算法,但也可以在启动时使用 zswap.compressor 更改压缩算法。其他选项包括 deflate, lz4hc, lzo, lzo-rle, 842zstd

使用 sysfs 在运行时更改压缩不会有问题,但在本例中,zswap 从 lz4 开始,并在稍后阶段切换到定义的算法。要立即使用另一种算法启动 zswap,必须通过内核引导参数进行设定,并且内核必须尽早加载相应的模块。这可以通过以下步骤来实现:

  1. 将与所选压缩器相关的模块添加到 mkinitcpio#模块(MODULES) 数组中。
  2. 修改 mkinitcpio 配置后重新生成 ramdisk 环境:见 mkinitcpio#创建和启用镜像
  3. 内核参数中将 zswap.compressor 设定为你选择的算法。

在下次启动时,见 #当前参数来检查 zswap 现在是否使用了请求的压缩器。

参考文章

使用 zSWAP、zstd 和 z3fold 来提高操作系统的性能
zswap - Arch Linux中文维基