zram:基于压缩内存的块设备¶
简介¶
zram 模块创建名为 /dev/zram<id>(<id> = 0, 1, ...)的基于内存的块设备。写入这些磁盘的页面会被压缩并存储在内存本身中。这些磁盘允许非常快的 I/O,并且压缩提供了良好的内存节省。一些用例包括 /tmp 存储、用作交换磁盘、/var 下的各种缓存,可能还有更多。 :)
各个 zram 设备的统计信息通过 sysfs 节点在 /sys/block/zram<id>/ 导出
用法¶
有几种方法可以配置和管理 zram 设备
使用 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 3 > /sys/block/zram0/max_comp_streams
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) 设置最大压缩流数¶
无论传递给此属性的值如何,ZRAM 始终会分配多个压缩流 - 每个在线 CPU 一个 - 从而允许多个并发压缩操作。当某些 CPU 变为脱机状态时,分配的压缩流数量会减少。除非您运行的是 UP 系统或只有一个 CPU 在线,否则不再有单压缩流模式。
要找出当前有多少个可用的流
cat /sys/block/zram0/max_comp_streams
3) 选择压缩算法¶
使用 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 支持的压缩算法。
4) 设置压缩算法参数:可选¶
压缩算法可能支持可以针对特定数据集进行调整的特定参数。ZRAM 具有一个 algorithm_params 设备属性,该属性提供每个算法的参数配置。
例如,一些压缩算法支持 level 参数。此外,某些压缩算法支持预训练字典,这会显着改变算法的特性。为了配置压缩算法以使用外部预训练字典,请将 dict 的完整路径与其他参数一起传递
#pass path to pre-trained zstd dictionary
echo "algo=zstd dict=/etc/dictioary" > /sys/block/zram0/algorithm_params
#same, but using algorithm priority
echo "priority=1 dict=/etc/dictioary" > \
/sys/block/zram0/algorithm_params
#pass path to pre-trained zstd dictionary and compression level
echo "algo=zstd level=8 dict=/etc/dictioary" > \
/sys/block/zram0/algorithm_params
参数是算法特定的:并非所有算法都支持预训练字典,并非所有算法都支持 level。此外,对于某些算法,level 控制压缩级别(值越高,压缩率越好,对于某些算法甚至可以取负值),对于其他算法,level 是加速级别(值越高,压缩率越低)。
5) 设置磁盘大小¶
通过将值写入 sysfs 节点 “disksize” 来设置磁盘大小。该值可以是字节,也可以使用内存后缀。示例
# 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 是浪费的。
6) 设置内存限制:可选¶
通过将值写入 sysfs 节点 “mem_limit” 来设置内存限制。该值可以是字节,也可以使用内存后缀。此外,您可以在运行时更改该值。示例
# 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
7) 激活¶
mkswap /dev/zram0
swapon /dev/zram0
mkfs.ext4 /dev/zram1
mount /dev/zram1 /tmp
8) 添加/删除 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
9) 统计信息¶
每个设备的统计信息都作为 /sys/block/zram<id>/ 下的各种节点导出
以下是导出的设备属性的简要描述。有关更多详细信息,请阅读 Documentation/ABI/testing/sysfs-block-zram。
名称 |
访问 |
描述 |
---|---|---|
disksize |
读写 |
显示和设置设备的磁盘大小 |
initstate |
只读 |
显示设备的初始化状态 |
reset |
只写 |
触发设备重置 |
mem_used_max |
只写 |
重置 mem_used_max 计数器(稍后介绍) |
mem_limit |
只写 |
指定 ZRAM 可以用来存储压缩数据的最大内存量 |
writeback_limit |
只写 |
指定 zram 可以以 4KB 为单位写出到后备设备的最大写入 IO 量 |
writeback_limit_enable |
读写 |
显示和设置 writeback_limit 功能 |
max_comp_streams |
读写 |
可能同时进行的压缩操作数量 |
comp_algorithm |
读写 |
显示和更改压缩算法 |
algorithm_params |
只写 |
设置压缩算法参数 |
compact |
只写 |
触发内存压缩 |
debug_stat |
只读 |
此文件用于 zram 调试目的 |
backing_dev |
读写 |
为 zram 设置用于写出的后端存储 |
idle |
只写 |
将分配的槽标记为空闲 |
建议用户空间使用以下文件来读取设备统计信息。
文件 /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 文件表示设备的内存统计信息。它由一行文本组成,包含以下由空格分隔的统计信息
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 字节
10) 取消激活¶
swapoff /dev/zram0
umount /dev/zram1
11) 重置¶
向“reset” sysfs 节点写入任何正值
echo 1 > /sys/block/zram0/reset echo 1 > /sys/block/zram1/reset这将释放为给定设备分配的所有内存,并将磁盘大小重置为零。在重新使用设备之前,必须再次设置磁盘大小。
可选功能¶
回写¶
使用 CONFIG_ZRAM_WRITEBACK,zram 可以将空闲/不可压缩页面写入后端存储,而不是将其保存在内存中。要使用此功能,管理员应通过以下方式设置后端设备
echo /dev/sda5 > /sys/block/zramX/backing_dev
在设置磁盘大小之前。目前它仅支持分区。如果管理员想要使用不可压缩页面回写,他们可以通过以下方式进行
echo huge > /sys/block/zramX/writeback
要使用空闲页面回写,首先,用户需要将 zram 页面声明为空闲
echo all > /sys/block/zramX/idle
从现在开始,zram 上的任何页面都是空闲页面。空闲标记将被删除,直到有人请求访问该块。也就是说,除非有访问请求,否则这些页面仍然是空闲页面。此外,当启用 CONFIG_ZRAM_TRACK_ENTRY_ACTIME 时,可以根据页面上次访问以来的时间(以秒为单位)将页面标记为空闲
echo 86400 > /sys/block/zramX/idle
在此示例中,超过 86400 秒(一天)未访问的所有页面都将被标记为空闲。
管理员可以在正确的时间通过以下方式请求回写这些空闲页面
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
如果闪存设备存在大量写 I/O,则可能会出现闪存磨损问题,因此管理员需要设计写入限制以保证整个产品生命周期的存储健康。
为了解决这个问题,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 的第三列知道它。
重新压缩¶
使用 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 旋钮: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
没有任何(包括辅助)算法可以压缩它
上面示例的第一行表示第 300 个块在 75.033841 秒被访问,并且该块的状态是巨大的,因此它被回写到后端存储。这是一个调试功能,因此任何人都不要依赖它来正常工作。
Nitin Gupta ngupta@vflare.org