zswap¶
概述¶
Zswap 是一个用于交换页面的轻量级压缩缓存。它会获取正在被换出的页面,并尝试将它们压缩到动态分配的基于 RAM 的内存池中。zswap 基本上是用 CPU 周期来换取潜在减少的交换 I/O。如果从压缩缓存中读取的速度快于从交换设备读取的速度,这种权衡也会带来显著的性能提升。
一些潜在的好处
RAM 容量有限的台式机/笔记本电脑用户可以减轻交换带来的性能影响。
共享通用 I/O 资源的过度提交的虚拟机可以显著降低其交换 I/O 压力,从而避免虚拟机监控程序进行粗暴的 I/O 节流。这允许完成更多工作,同时减少对虚拟机工作负载和共享 I/O 子系统的虚拟机的影响
使用 SSD 作为交换设备的用户可以通过大幅减少缩短寿命的写入操作来延长设备的使用寿命。
当压缩池达到其大小限制时,Zswap 会以 LRU 为基础将页面从压缩缓存中逐出到后备交换设备。此要求已在之前的社区讨论中确定。
是否在启动时启用 Zswap 取决于是否启用 CONFIG_ZSWAP_DEFAULT_ON
Kconfig 选项。然后可以通过提供内核命令行 zswap.enabled=
选项来覆盖此设置,例如 zswap.enabled=0
。也可以使用 sysfs 接口在运行时启用和禁用 Zswap。在运行时启用 zswap 的示例命令(假设 sysfs 安装在 /sys
上)是
echo 1 > /sys/module/zswap/parameters/enabled
当在运行时禁用 zswap 时,它将停止存储正在被换出的页面。但是,它不会立即写出或故障返回到内存中压缩池中存储的所有页面。存储在 zswap 中的页面将保留在压缩池中,直到它们失效或故障返回到内存中。为了强制所有页面离开压缩池,对交换设备执行 swapoff 将会将所有换出的页面(包括压缩池中的页面)故障返回到内存中。
设计¶
Zswap 从交换子系统接收要压缩的页面,并且能够在其自己的压缩池已满的情况下,以 LRU 为基础将页面从其自己的压缩池中逐出,并将它们写回后备交换设备。
Zswap 使用 zpool 来管理压缩内存池。zpool 中的每个分配都不能通过地址直接访问。而是,分配例程会返回一个句柄,并且必须先映射该句柄才能访问。压缩内存池按需增长,并随着压缩页面的释放而缩小。该池不是预先分配的。默认情况下,会创建一个在 CONFIG_ZSWAP_ZPOOL_DEFAULT
Kconfig 选项中选择的 zpool 类型,但是可以通过设置 zpool
属性(例如 zswap.zpool=zbud
)在启动时覆盖它。也可以使用 sysfs zpool
属性在运行时更改它,例如
echo zbud > /sys/module/zswap/parameters/zpool
zbud 类型的 zpool 分配正好 1 个页面来存储 2 个压缩页面,这意味着压缩率始终为 2:1 或更差(因为半满的 zbud 页面)。zsmalloc 类型的 zpool 具有更复杂的压缩页面存储方法,并且可以实现更高的存储密度。
当交换页面从换出传递到 zswap 时,zswap 会维护交换条目的映射(交换类型和交换偏移量的组合)到引用该压缩交换页面的 zpool 句柄。此映射是通过每个交换类型的红黑树实现的。交换偏移量是树节点的搜索键。
在 PTE 上发生页面错误(PTE 是交换条目)期间,swapin 代码会调用 zswap 加载函数将页面解压缩到页面错误处理程序分配的页面中。
一旦没有 PTE 引用存储在 zswap 中的交换页面(即 swap_map 中的计数变为 0),交换代码就会调用 zswap 失效函数以释放压缩的条目。
Zswap 力求在策略上保持简单。Sysfs 属性允许一个用户控制策略
max_pool_percent - 压缩池可以占用的最大内存百分比。
默认压缩器在 CONFIG_ZSWAP_COMPRESSOR_DEFAULT
Kconfig 选项中选择,但是可以通过设置 compressor
属性(例如 zswap.compressor=lzo
)在启动时覆盖它。也可以使用 sysfs “compressor” 属性在运行时更改它,例如
echo lzo > /sys/module/zswap/parameters/compressor
当在运行时更改 zpool 和/或压缩器参数时,任何现有的压缩页面都不会被修改;它们保留在它们自己的 zpool 中。当请求旧 zpool 中的页面时,会使用其原始压缩器将其解压缩。一旦所有页面都从旧 zpool 中删除,该 zpool 及其压缩器将被释放。
zswap 中的某些页面是相同值填充的页面(即,页面的内容具有相同的值或重复的模式)。这些页面包括零填充的页面,它们的处理方式不同。在存储操作期间,在压缩页面之前会检查页面是否为相同值填充的页面。如果为真,则将页面的压缩长度设置为零,并存储模式或相同填充值。
为了防止 zswap 在 zswap 已满且交换压力很大时缩小池(这将导致页面在 zswap 池中翻转,而没有任何实际好处,但会导致系统性能下降),引入了一个特殊的参数来实现某种滞后现象,拒绝将页面放入 zswap 池,除非它有足够的空间(如果已达到限制)。要设置 zswap 在其已满后将再次开始接受页面的阈值,请使用 sysfs accept_threshold_percent
属性,例如
echo 80 > /sys/module/zswap/parameters/accept_threshold_percent
将此参数设置为 100 将禁用滞后。
某些用户无法容忍 zswap 存储失败和 zswap 写回带来的交换。可以在 cgroup 的基础上完全禁用交换(而不禁用 zswap 本身),如下所示
echo 0 > /sys/fs/cgroup/<cgroup-name>/memory.zswap.writeback
请注意,如果存储失败反复发生(例如,如果页面不可压缩),用户可以在禁用写回后观察到回收效率低下(因为相同的页面可能会一次又一次地被拒绝)。
当 zswap 池中存在大量冷内存时,主动将这些冷页面写入交换并回收内存以用于其他用例可能是有利的。默认情况下,zswap 收缩器处于禁用状态。用户可以按如下方式启用它
echo Y > /sys/module/zswap/parameters/shrinker_enabled
如果选择 CONFIG_ZSWAP_SHRINKER_DEFAULT_ON
,则可以在启动时启用此功能。
提供了一个 debugfs 接口,用于显示有关池大小、存储的页面数、相同值填充的页面以及页面被拒绝的各种原因的各种统计信息。