zram:基于压缩 RAM 的块设备¶
简介¶
zram 模块创建名为 /dev/zram<id> (<id> = 0, 1, ...) 的基于 RAM 的块设备。写入这些磁盘的页面会被压缩并存储在内存本身中。这些磁盘允许非常快的 I/O,并且压缩提供良好的内存节省。一些用例包括 /tmp 存储、用作交换磁盘、/var 下的各种缓存,可能还有更多。 :)
各个 zram 设备的统计信息通过 /sys/block/zram<id>/ 处的 sysfs 节点导出。
用法¶
有几种方法可以配置和管理 zram 设备(-s)
使用 zram 和 zram_control sysfs 属性
使用 util-linux 提供的 zramctl 实用程序 (util-linux@vger.kernel.org)。
在本文档中,我们将仅描述“手动” zram 配置步骤,即 zram 和 zram_control sysfs 属性。
为了更好地了解 zramctl,请查阅 util-linux 文档、zramctl 手册页或 zramctl --help。请注意,zram 维护者不开发/维护 util-linux 或 zramctl,如果您有任何问题,请联系 util-linux@vger.kernel.org
以下显示了使用 zram 的典型步骤序列。
警告¶
为了简单起见,我们在下面的大多数示例中跳过了错误检查部分。但是,您有责任处理错误。
如果出现错误,zram sysfs 属性总是返回负值。可能的返回代码列表
-EBUSY |
尝试修改一旦设备初始化后就无法更改的属性。请先重置设备。 |
-ENOMEM |
zram 无法分配足够的内存来满足您的需求。 |
-EINVAL |
提供了无效的输入。 |
-EAGAIN |
稍后重试操作(例如,尝试同时运行重新压缩和写回时)。 |
如果您使用 'echo',则返回值由 'echo' 实用程序设置,并且在一般情况下,类似
echo foo > /sys/block/zram0/comp_algorithm
if [ $? -ne 0 ]; then
handle_error
fi
应该足够了。
1) 加载模块¶
modprobe zram num_devices=4
这将创建 4 个设备:/dev/zram{0,1,2,3}
num_devices 参数是可选的,它告诉 zram 应预先创建多少个设备。默认值:1。
2) 选择压缩算法¶
使用 comp_algorithm 设备属性,您可以查看可用和当前选定的(显示在方括号中)压缩算法,或更改选定的压缩算法(一旦设备初始化,就无法更改压缩算法)。
示例
#show supported compression algorithms
cat /sys/block/zram0/comp_algorithm
lzo [lz4]
#select lzo compression algorithm
echo lzo > /sys/block/zram0/comp_algorithm
目前,comp_algorithm 内容仅显示 zram 支持的压缩算法。
3) 设置压缩算法参数:可选¶
压缩算法可能支持特定的参数,可以针对特定的数据集进行调整。 ZRAM 有一个 algorithm_params 设备属性,它提供每个算法的参数配置。
例如,几种压缩算法支持 level 参数。此外,某些压缩算法支持预训练的字典,这会显着改变算法的特性。为了配置压缩算法以使用外部预训练字典,请传递到 dict 的完整路径以及其他参数
#pass path to pre-trained zstd dictionary
echo "algo=zstd dict=/etc/dictionary" > /sys/block/zram0/algorithm_params
#same, but using algorithm priority
echo "priority=1 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
#pass path to pre-trained zstd dictionary and compression level
echo "algo=zstd level=8 dict=/etc/dictionary" > \
/sys/block/zram0/algorithm_params
参数是算法特定的:并非所有算法都支持预训练的字典,并非所有算法都支持 level。此外,对于某些算法,level 控制压缩级别(值越高,压缩率越好,对于某些算法,它甚至可以采用负值),对于其他算法,level 是加速级别(值越高,压缩率越低)。
4) 设置磁盘大小¶
通过将值写入 sysfs 节点 'disksize' 来设置磁盘大小。该值可以是字节,也可以使用 mem 后缀。示例
# Initialize /dev/zram0 with 50MB disksize
echo $((50*1024*1024)) > /sys/block/zram0/disksize
# Using mem suffixes
echo 256K > /sys/block/zram0/disksize
echo 512M > /sys/block/zram0/disksize
echo 1G > /sys/block/zram0/disksize
注意:创建一个大于内存大小两倍的 zram 意义不大,因为我们期望 2:1 的压缩率。请注意,zram 在不使用时会占用大约 0.1% 的磁盘大小,因此巨大的 zram 是浪费的。
5) 设置内存限制:可选¶
通过将值写入 sysfs 节点 'mem_limit' 来设置内存限制。该值可以是字节,也可以使用 mem 后缀。此外,您可以在运行时更改该值。示例
# limit /dev/zram0 with 50MB memory
echo $((50*1024*1024)) > /sys/block/zram0/mem_limit
# Using mem suffixes
echo 256K > /sys/block/zram0/mem_limit
echo 512M > /sys/block/zram0/mem_limit
echo 1G > /sys/block/zram0/mem_limit
# To disable memory limit
echo 0 > /sys/block/zram0/mem_limit
6) 激活¶
mkswap /dev/zram0
swapon /dev/zram0
mkfs.ext4 /dev/zram1
mount /dev/zram1 /tmp
7) 添加/删除 zram 设备¶
zram 提供了一个控制接口,可以动态(按需)添加和删除设备。
为了添加新的 /dev/zramX 设备,请对 hot_add 属性执行读取操作。这将返回新设备的设备 ID(意味着您可以使用 /dev/zram<id>)或错误代码。
示例
cat /sys/class/zram-control/hot_add
1
要删除现有的 /dev/zramX 设备(其中 X 是设备 ID),请执行
echo X > /sys/class/zram-control/hot_remove
8) 统计信息¶
每个设备的统计信息作为 /sys/block/zram<id>/ 下的各种节点导出
下面是导出的设备属性的简要说明。有关更多详细信息,请阅读 ABI 文件测试/sysfs-block-zram。
名称 |
访问 |
描述 |
---|---|---|
disksize |
RW |
显示和设置设备的磁盘大小 |
initstate |
RO |
显示设备的初始化状态 |
reset |
WO |
触发设备重置 |
mem_used_max |
WO |
重置 mem_used_max 计数器(稍后会介绍) |
mem_limit |
WO |
指定 ZRAM 可以用来存储压缩数据的最大内存量 |
writeback_limit |
WO |
指定 zram 可以以 4KB 为单位写入后备设备的最大写入 IO 量 |
writeback_limit_enable |
RW |
显示和设置 writeback_limit 功能 |
comp_algorithm |
RW |
显示和更改压缩算法 |
algorithm_params |
WO |
设置压缩算法参数 |
compact |
WO |
触发内存压缩 |
debug_stat |
RO |
此文件用于 zram 调试目的 |
backing_dev |
RW |
设置 zram 写入的后端存储 |
idle |
WO |
将分配的插槽标记为空闲 |
建议用户空间使用以下文件来读取设备统计信息。
文件 /sys/block/zram<id>/stat
表示块层统计信息。阅读 /sys/block/<dev>/stat 中的块层统计信息 了解详细信息。
文件 /sys/block/zram<id>/io_stat
stat 文件表示设备未由块层统计的 I/O 统计信息,因此在 zram<id>/stat 文件中不可用。它由单行文本组成,包含以下以空格分隔的统计信息
failed_reads
失败的读取次数
failed_writes
失败的写入次数
invalid_io
非页面大小对齐的 I/O 请求数
notify_free
根据设备的使用场景,它可以统计
由于交换插槽释放通知而释放的页面数
由于 bio 发送的 REQ_OP_DISCARD 请求而释放的页面数。前者在交换插槽被释放时发送到交换块设备,这意味着该磁盘正在用作交换磁盘。
后者由以 discard 选项挂载的文件系统发送,每当一些数据块被丢弃时。
文件 /sys/block/zram<id>/mm_stat
mm_stat 文件表示设备的 mm 统计信息。它由单行文本组成,包含以下以空格分隔的统计信息
orig_data_size
存储在此磁盘中的数据的未压缩大小。单位:字节
compr_data_size
存储在此磁盘中的数据的压缩大小
mem_used_total
为此磁盘分配的内存量。这包括为此磁盘分配的分配器碎片和元数据开销。因此,可以使用 compr_data_size 和此统计信息来计算分配器空间效率。单位:字节
mem_limit
ZRAM 可以用来存储压缩数据的最大内存量
mem_used_max
zram 已消耗的最大内存量,用于存储数据
same_pages
写入此磁盘的相同元素填充页面的数量。没有为此类页面分配内存。
pages_compacted
压缩期间释放的页面数
huge_pages
不可压缩的页面数
huge_pages_since
自 zram 设置以来不可压缩的页面数
文件 /sys/block/zram<id>/bd_stat
bd_stat 文件表示设备的后备设备统计信息。它由单行文本组成,包含以下以空格分隔的统计信息
bd_count
写入后备设备的数据大小。单位:4K 字节
bd_reads
从后备设备读取的次数。单位:4K 字节
bd_writes
写入后备设备的次数。单位:4K 字节
9) 停用¶
swapoff /dev/zram0
umount /dev/zram1
10) 重置¶
将任何正值写入 'reset' sysfs 节点
echo 1 > /sys/block/zram0/reset echo 1 > /sys/block/zram1/reset这将释放为给定设备分配的所有内存,并将磁盘大小重置为零。在重新使用设备之前,您必须再次设置磁盘大小。
可选功能¶
IDLE 页面跟踪¶
zram 内置了对空闲页面跟踪(即已分配但未使用的页面)的支持。此功能对于例如 zram 写回和重新压缩很有用。为了将页面标记为空闲,请执行以下命令
echo all > /sys/block/zramX/idle
这将把所有已分配的 zram 页面标记为空闲。只有在访问页面(块)(例如,被覆盖或释放)时才会删除空闲标记。此外,当启用 CONFIG_ZRAM_TRACK_ENTRY_ACTIME 时,可以根据自上次访问特定 zram 页面以来经过的秒数将页面标记为空闲
echo 86400 > /sys/block/zramX/idle
在此示例中,所有超过 86400 秒(一天)未被访问的页面都将被标记为空闲。
写回¶
使用 CONFIG_ZRAM_WRITEBACK,zram 可以将空闲/不可压缩的页面写入后备存储,而不是将其保存在内存中。要使用此功能,管理员应通过以下方式设置后备设备
echo /dev/sda5 > /sys/block/zramX/backing_dev
在设置 disksize 之前。目前仅支持分区。如果管理员想要使用不可压缩的页面写回,他们可以通过以下方式进行
echo huge > /sys/block/zramX/writeback
管理员可以在正确的时间通过以下方式请求写回空闲页面
echo idle > /sys/block/zramX/writeback
使用该命令,zram 会将空闲页面从内存写回到存储。
此外,如果用户选择仅写回巨大和空闲页面,则可以通过以下方式完成
echo huge_idle > /sys/block/zramX/writeback
如果用户选择仅写回不可压缩的页面(没有任何算法可以压缩的页面),则可以通过以下方式完成
echo incompressible > /sys/block/zramX/writeback
如果管理员想要将 zram 设备中的特定页面写入后备设备,他们可以将页面索引写入该接口
echo "page_index=1251" > /sys/block/zramX/writeback
在 Linux 6.16 中,此接口进行了一些修改。首先,该接口现在支持所有参数的 key=value 格式(type=huge_idle 等)。其次,引入了对 page_indexes 的支持,它指定要写回的页面的 LOW-HIGH 范围(或范围)。这减少了 syscall 的数量,但更重要的是,这实现了最佳的后处理目标选择策略。用法示例
echo "type=idle" > /sys/block/zramX/writeback
echo "page_indexes=1-100 page_indexes=200-300" > \
/sys/block/zramX/writeback
我们现在还允许每个调用使用多个 page_index 参数,以及单个页面和页面范围的混合
echo page_index=42 page_index=99 page_indexes=100-200 \
page_indexes=500-700 > /sys/block/zramX/writeback
如果有大量使用闪存设备的写入 IO,则可能会出现闪存磨损问题,因此管理员需要设计写入限制,以保证整个产品生命周期的存储健康。
为了克服此问题,zram 支持“writeback_limit”功能。“writeback_limit_enable”的默认值为 0,因此它不限制任何写回。也就是说,如果管理员想要应用写回预算,他们应该通过以下方式启用 writeback_limit_enable
$ echo 1 > /sys/block/zramX/writeback_limit_enable
一旦设置了 writeback_limit_enable,zram 就不允许任何写回,直到管理员通过 /sys/block/zramX/writeback_limit 设置预算。
(如果管理员不启用 writeback_limit_enable,则通过 /sys/block/zramX/writeback_limit 分配的 writeback_limit 值没有意义。)
如果管理员想要将每天的写回限制为 400M,他们可以像下面这样做
$ MB_SHIFT=20
$ 4K_SHIFT=12
$ echo $((400<<MB_SHIFT>>4K_SHIFT)) > \
/sys/block/zram0/writeback_limit.
$ echo 1 > /sys/block/zram0/writeback_limit_enable
如果管理员想要在预算耗尽后再次允许进一步写入,他们可以像下面这样做
$ echo $((400<<MB_SHIFT>>4K_SHIFT)) > \
/sys/block/zram0/writeback_limit
如果管理员想要查看自上次设置以来剩余的写回预算
$ cat /sys/block/zramX/writeback_limit
如果管理员想要禁用写回限制,他们可以这样做
$ echo 0 > /sys/block/zramX/writeback_limit_enable
每当您重置 zram 时(例如,系统重启,echo 1 > /sys/block/zramX/reset),writeback_limit 计数将重置,因此,保留在重置 zram 之前发生的写回次数,以便在下次设置中分配额外的写回预算是用户的工作。
如果管理员想要测量某个时间段内的写回计数,他们可以通过 /sys/block/zram0/bd_stat 的第 3 列了解它。
重新压缩¶
使用 CONFIG_ZRAM_MULTI_COMP,zram 可以使用备用(辅助)压缩算法重新压缩页面。基本思想是备用压缩算法可以提供更好的压缩率,但代价是(可能)较慢的压缩/解压缩速度。备用压缩算法可以更成功地压缩大页面(那些默认算法无法压缩的页面)。另一种应用是空闲页面重新压缩 - 可以使用更有效的算法重新压缩冷且位于内存中的页面,从而减少 zsmalloc 内存使用。
使用 CONFIG_ZRAM_MULTI_COMP,zram 支持最多 4 种压缩算法:一种主算法和最多 3 种辅助算法。主 zram 压缩器在“3) 选择压缩算法”中进行了解释,辅助算法使用 recomp_algorithm 设备属性进行配置。
示例:
#show supported recompression algorithms
cat /sys/block/zramX/recomp_algorithm
#1: lzo lzo-rle lz4 lz4hc [zstd]
#2: lzo lzo-rle lz4 [lz4hc] zstd
备用压缩算法按优先级排序。在上面的示例中,zstd 用作第一种备用算法,其优先级为 1,而 lz4hc 配置为优先级为 2 的压缩算法。备用压缩算法的优先级在算法配置期间提供:
#select zstd recompression algorithm, priority 1
echo "algo=zstd priority=1" > /sys/block/zramX/recomp_algorithm
#select deflate recompression algorithm, priority 2
echo "algo=deflate priority=2" > /sys/block/zramX/recomp_algorithm
CONFIG_ZRAM_MULTI_COMP 启用的另一个设备属性是 recompress,它控制重新压缩。
示例:
#IDLE pages recompression is activated by `idle` mode
echo "type=idle" > /sys/block/zramX/recompress
#HUGE pages recompression is activated by `huge` mode
echo "type=huge" > /sys/block/zram0/recompress
#HUGE_IDLE pages recompression is activated by `huge_idle` mode
echo "type=huge_idle" > /sys/block/zramX/recompress
空闲页面的数量可能很大,因此用户空间可以将大小阈值(以字节为单位)传递给 recompress knob:zram 将仅重新压缩大小等于或大于该大小的页面:
#recompress all pages larger than 3000 bytes
echo "threshold=3000" > /sys/block/zramX/recompress
#recompress idle pages larger than 2000 bytes
echo "type=idle threshold=2000" > /sys/block/zramX/recompress
也可以限制 zram 重新压缩将尝试重新压缩的页面数量:
echo "type=huge_idle max_pages=42" > /sys/block/zramX/recompress
在重新压缩期间,对于每个符合重新压缩标准的页面,ZRAM 按照其优先级的顺序迭代已注册的备用压缩算法列表。当重新压缩成功(重新压缩的对象小于原始对象)并符合重新压缩标准(例如,大小阈值)或没有剩余的辅助算法可尝试时,ZRAM 停止。如果没有辅助算法可以成功地重新压缩页面,则将该页面标记为不可压缩,因此 ZRAM 将来不会尝试重新压缩它。
当它迭代已注册的压缩算法列表时,这种重新压缩行为增加了我们找到成功压缩特定页面的算法的机会。但是,有时限制重新压缩到仅一种特定算法是很方便(有时甚至是必要的),这样它就不会尝试任何其他算法。这可以通过提供 algo 或 priority 参数来实现:
#use zstd algorithm only (if registered)
echo "type=huge algo=zstd" > /sys/block/zramX/recompress
#use zstd algorithm only (if zstd was registered under priority 1)
echo "type=huge priority=1" > /sys/block/zramX/recompress
内存跟踪¶
使用 CONFIG_ZRAM_MEMORY_TRACKING,用户可以了解 zram 块的信息。这可能有助于使用 *pagemap 捕获进程的冷页面或不可压缩页面。
如果启用此功能,您可以通过 /sys/kernel/debug/zram/zram0/block_state 查看块状态”。输出如下
300 75.033841 .wh...
301 63.806904 s.....
302 63.806919 ..hi..
303 62.801919 ....r.
304 146.781902 ..hi.n
- 第一列
zram 的块索引。
- 第二列
自系统启动以来的访问时间
- 第三列
块的状态
- s
相同页面
- w
已写入后备存储的页面
- h
大页面
- i
空闲页面
- r
重新压缩的页面(辅助压缩算法)
- n
没有任何(包括辅助)算法可以压缩它
上面示例的第一行表示在 75.033841 秒访问了第 300 个块,并且该块的状态很大,因此它被写回到后备存储。这是一个调试功能,因此任何人都不应依赖它来正常工作。
Nitin Gupta ngupta@vflare.org