zram:基于压缩的RAM块设备 【ChatGPT】

发布时间 2023-12-11 19:35:26作者: 摩斯电码

zram:基于压缩的RAM块设备

介绍

zram模块创建名为/dev/zram<id><id> = 0, 1, ...)的基于RAM的块设备。写入这些磁盘的页面会被压缩并存储在内存中。这些磁盘允许非常快速的I/O,并且压缩提供了很好的内存节省。一些用例包括/tmp存储、用作交换磁盘、/var下的各种缓存,以及可能还有其他用途。 ?

单个zram设备的统计信息通过/sys/block/zram<id>/下的sysfs节点导出。

用法

有几种方法可以配置和管理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:提供了无效的输入。

如果您使用'echo',则返回的值由'echo'实用程序设置,在一般情况下,可以使用以下类似的代码来处理错误:

echo 3 > /sys/block/zram0/max_comp_streams
if [ $? -ne 0 ]; then
    处理错误
fi

1) 加载模块

modprobe zram num_devices=4

这将创建4个设备:/dev/zram{0,1,2,3}

num_devices参数是可选的,告诉zram应预先创建多少个设备。默认值为1。

2) 设置最大压缩流的数量

无论传递给此属性的值如何,ZRAM始终会分配多个压缩流 - 每个在线CPU一个 - 从而允许多个并发的压缩操作。当一些CPU下线时,分配的压缩流数量会减少。除非您运行的是UP系统或只有1个CPU在线,否则不再存在单一压缩流模式。

要查看当前有多少个流可用:

cat /sys/block/zram0/max_comp_streams

3) 选择压缩算法

使用comp_algorithm设备属性,可以查看可用的压缩算法和当前选择的(用方括号显示),或更改所选的压缩算法(一旦设备初始化,就无法更改压缩算法)。

示例:

# 显示支持的压缩算法
cat /sys/block/zram0/comp_algorithm
lzo [lz4]

# 选择lzo压缩算法
echo lzo > /sys/block/zram0/comp_algorithm

目前,comp_algorithm内容不一定显示内核支持的每种压缩算法。我们主要保留此列表以简化设备配置,可以使用comp_algorithm配置不在列表中的压缩算法的新设备。问题在于,内部使用Crypto API,如果某些算法被构建为模块,就不可能使用例如/proc/crypto或任何其他方法列出所有这些算法。然而,这样做的好处是允许使用自定义的加密压缩模块(实现S/W或H/W压缩)。

4) 设置磁盘大小

通过将值写入sysfs节点'disksize'来设置磁盘大小。该值可以是字节,也可以使用内存后缀。示例:

# 使用50MB的磁盘大小初始化/dev/zram0
echo $((50*1024*1024)) > /sys/block/zram0/disksize

# 使用内存后缀
echo 256K > /sys/block/zram0/disksize
echo 512M > /sys/block/zram0/disksize
echo 1G > /sys/block/zram0/disksize

注意:创建大于内存大小两倍的zram几乎没有意义,因为我们期望有2:1的压缩比。请注意,当不使用zram时,zram使用的磁盘大小约为磁盘大小的0.1%,因此巨大的zram是浪费的。

5) 设置内存限制:可选

通过将值写入sysfs节点'mem_limit'来设置内存限制。该值可以是字节,也可以使用内存后缀。此外,您可以在运行时更改该值。示例:

# 限制/dev/zram0的内存为50MB
echo $((50*1024*1024)) > /sys/block/zram0/mem_limit

# 使用内存后缀
echo 256K > /sys/block/zram0/mem_limit
echo 512M > /sys/block/zram0/mem_limit
echo 1G > /sys/block/zram0/mem_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>/ 下。

以下是导出设备属性的简要描述。更多细节请阅读 Documentation/ABI/testing/sysfs-block-zram。

  • disksize:读写属性,显示和设置设备的磁盘大小
  • initstate:只读属性,显示设备的初始化状态
  • reset:写入属性,触发设备重置
  • mem_used_max:写入属性,重置 mem_used_max 计数器(见后文)
  • mem_limit:写入属性,指定 ZRAM 可用于存储压缩数据的最大内存量
  • writeback_limit:写入属性,指定 zram 可以写出到后备设备的最大写入 IO 量(以 4KB 为单位)
  • writeback_limit_enable:读写属性,显示和设置 writeback_limit 功能
  • max_comp_streams:读写属性,可能的并发压缩操作数
  • comp_algorithm:读写属性,显示和更改压缩算法
  • compact:写入属性,触发内存压缩
  • debug_stat:只读属性,用于 zram 调试目的
  • backing_dev:读写属性,为 zram 设置后端存储以写出
  • idle:写入属性,将分配的插槽标记为空闲

建议用户空间使用以下文件来读取设备统计信息。

  • 文件 /sys/block/zram<id>/stat:表示块层统计信息。有关详细信息,请阅读 /sys/block/<dev>/stat 中的块层统计信息。
  • 文件 /sys/block/zram<id>/io_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 统计信息。它由一行文本组成,包含以下由空格分隔的统计信息:
    • 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_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

这将释放为给定设备分配的所有内存,并将磁盘大小重置为零。在重新使用设备之前,您必须重新设置磁盘大小。

可选的特性

回写

CONFIG_ZRAM_WRITEBACK是Linux内核中zram模块的一个可选功能,它允许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_MEMORY_TRACKING时,页面可以根据它们自上次访问以来的时间(以秒为单位)被标记为空闲:

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

如果使用闪存设备进行大量写入操作,可能会出现闪存耗尽的问题,因此管理员需要设计写入限制以保证整个产品寿命的存储健康。

为了解决这个问题,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分配的值是无意义的。)

如果管理员希望将写回限制为每天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设备属性进行配置的。

示例:

#显示支持的重新压缩算法
cat /sys/block/zramX/recomp_algorithm
#1: lzo lzo-rle lz4 lz4hc [zstd]
#2: lzo lzo-rle lz4 [lz4hc] zstd

备用压缩算法按优先级排序。在上面的示例中,zstd被用作第一个备用算法,其优先级为1,而lz4hc被配置为优先级为2的压缩算法。在算法配置期间,提供了备用压缩算法的优先级:

#选择zstd重新压缩算法,优先级1
echo "algo=zstd priority=1" > /sys/block/zramX/recomp_algorithm

#选择deflate重新压缩算法,优先级2
echo "algo=deflate priority=2" > /sys/block/zramX/recomp_algorithm

CONFIG_ZRAM_MULTI_COMP启用的另一个设备属性是recompress,它控制重新压缩。

示例:

#通过`idle`模式激活空闲页面重新压缩
echo "type=idle" > /sys/block/zramX/recompress

#通过`huge`模式激活巨大页面重新压缩
echo "type=huge" > /sys/block/zram0/recompress

#通过`huge_idle`模式激活巨大空闲页面重新压缩
echo "type=huge_idle" > /sys/block/zramX/recompress

空闲页面的数量可能很大,因此用户空间可以将大小阈值(以字节为单位)传递给recompress旋钮:zram将仅重新压缩大小相等或更大的页面:

#重新压缩所有大于3000字节的页面
echo "threshold=3000" > /sys/block/zramX/recompress

#重新压缩大于2000字节的空闲页面
echo "type=idle threshold=2000" > /sys/block/zramX/recompress

空闲页面的重新压缩需要内存跟踪。

在重新压缩期间,对于每个符合重新压缩条件的页面,ZRAM按照它们的优先级顺序遍历注册的备用压缩算法列表。ZRAM会在重新压缩成功(重新压缩对象的大小小于原始对象)并符合重新压缩条件(例如大小阈值)时停止,或者当没有剩余的次要算法可尝试时停止。如果没有次要算法可以成功地重新压缩页面,则该页面被标记为不可压缩,因此ZRAM将不会尝试在将来重新压缩它。

当它遍历注册的压缩算法列表时,这种重新压缩行为增加了我们找到成功压缩特定页面的算法的机会。然而,有时限制重新压缩为仅使用一个特定算法是方便的(有时甚至是必要的),以便它不会尝试任何其他算法。这可以通过提供algo=NAME参数来实现:

#仅使用zstd算法(如果已注册)
echo "type=huge algo=zstd" > /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秒时被访问,并且该块的状态是巨大的,因此它被写回到后备存储。这是一个调试功能,因此任何人都不应该依赖它正常工作。