BlobFS 中的随机访问压缩

Fuchsia 中的 BlobFS 文件系统以透明方式压缩文件, 磁盘空间。BlobFS 支持多种不同的压缩策略, zstd 是默认值。

文件压缩有一个缺点,那就是它会阻止随机访问 文件。对于大多数压缩算法,都必须从 然后解压缩即可访问单个字节。

对于 BlobFS,当文件是按需分页时,这是一个特殊的挑战。需求 分页可将文件部分加载到内存中,从而节省系统内存 内存。完整文件压缩可防止文件只加载一部分。

Fuchsia 将压缩文件拆分为可独立运行的帧 文件。这使得 BlobFS 能够对压缩文件进行需求分页, 因为文件可以部分加载和解压缩。

本文档介绍了分块压缩格式的设计, 解释了它在 Fuchsia 中的用途。

设计目标和非目标

以下列出了分块压缩设计的动机 格式和库:

  • 随机访问解压缩。必须能够独立 解压缩数据帧,而无需解压缩整个文件。

  • 柔性环境解压缩 API。该库旨在 因此客户端可以精确控制哪些 进行解压缩的帧。这支持需求分页等使用场景 客户端 (BlobFS) 拥有有关访问模式的更多信息, 通过解压缩特定帧来精确控制预读和预提取。

    这与管理程度更高的 API 不同 帧包含哪些已解压缩的字节。

  • 可配置的帧大小。必须能够将 解压缩的数据帧,以满足不同应用场景和不同需求 要求。

  • 灵活的压缩布局。此格式支持更具特色的帧 布局,例如具有不统一的框架尺寸或对齐的框架 以适应将来需要更多 灵活性。例如,非统一的帧尺寸可用于改进 数据存放区域:将一起读取的数据分成一组较小的数据, 或更大的帧。

  • zstd 相当的压缩比。 由于 zstd 是 BlobFS 当前的默认压缩算法, zstd 的压缩比是基准 用于对分块压缩库进行基准测试开销 因此必须尽量减少额外的元数据

  • 可配置的压缩积极性。必须能够用于 降低压缩速度,从而获得更好的压缩比。

  • 跨平台库。用于压缩和解压缩的库 分块压缩归档必须在 Fuchsia 和 编译主机(例如 Linux)中运行,以便在编译过程中使用 工具链。这是在构建时压缩文件所必需的,例如 。

以下是非目标:

  • 与 zstd 的格式级别兼容性。分块压缩归档是 与 zstd 和常规 zstd 工具不具有格式兼容 不适用于分块压缩归档

分块压缩

归档文件格式

分块归档包含一个标头,该标头后跟零个或多个 zstd 帧。

标头描述存档格式,并包含一个还原表,该表用于 将压缩帧映射到解压缩空间。

      0     1     2     3     4     5     6     7
   +-----+-----+-----+-----+-----+-----+-----+-----+
 0 |                 Magic Number                  |
   +-----+-----+-----+-----+-----+-----+-----+-----+
 8 |  Version  |  Reserved |       Num Frames      |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
16 |    Header CRC32       |        Reserved       |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
24 |                    Reserved                   |  // Reserved must be zero.
   +-----+-----+-----+-----+-----+-----+-----+-----+
32 |                                               |
40 |                   Seek Table                  |
48 |                     Entry                     |
56 |                                               |
   +-----+-----+-----+-----+-----+-----+-----+-----+
.. |                                               |
.. |                   Seek Table                  |
.. |                     Entry                     |
.. |                                               |
   +-----+-----+-----+-----+-----+-----+-----+-----+

“Header CRC32”基于整个标头计算,包括查找 表格。

跳转表

每个跳转表条目描述压缩文件中的 空间,以及数据在解压缩后扩展到的什么位置。

   +-----+-----+-----+-----+-----+-----+-----+-----+
 0 |               Decompressed Offset             |
   +-----+-----+-----+-----+-----+-----+-----+-----+
 8 |                Decompressed Size              |
   +-----+-----+-----+-----+-----+-----+-----+-----+
16 |                Compressed Offset              |
   +-----+-----+-----+-----+-----+-----+-----+-----+
24 |                 Compressed Size               |
   +-----+-----+-----+-----+-----+-----+-----+-----+

还原表条目在解压缩空间中是连续的,但可能 不连续的。这是为了支持添加对齐和 填充,以提高存储空间访问效率。

一个跳转表最多可容纳 1023 个条目(产生 32KiB 标头)和 必须包含至少 1 个条目(这将生成 64 字节的标头)。通常情况下 压缩帧紧跟在定位表之后(但此格式支持 从超过定位表末尾的任意偏移量开始的压缩帧)。

还原表不变量
  • I0:第一个寻道表条目的解压缩偏移量必须为 0。

  • I1:第一个定位表条目的压缩偏移量必须大于 或 与标头尺寸相同

  • I2:每个条目的解压缩偏移量必须等于 上一帧(即到上一帧的解压缩后偏移 + 长度)。

  • I3:每个条目的压缩偏移量必须大于或等于末尾 (即上一帧的压缩后的大小) 偏移 + 长度)。

  • I4:每个条目的解压缩和压缩长度都必须为非零值。

  • I5:任何压缩帧都不得超过文件末尾。

压缩帧

文件中的每个压缩帧都是一个常规的 zstd 压缩帧。给定 帧会映射到解压缩文件中某个连续的字节块。

文件中未包含跳转表的任何字节范围都会被忽略。

不要求每一帧数据具有相同的解压缩大小, 但当前的压缩实现方式是将输入数据拆分为 相等的帧。在压缩过程中,可以配置帧的大小。

随机访问解压缩

跳转表用于查找包含给定解压后的帧 范围。在请求解压缩文件中的特定范围时,一个或多个 必须加载和解压缩压缩帧。

例如,请考虑以下包含三个帧的文件:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |              |
|                 |           |     +--------|
|                 |           +-----+        |
+-----------------+   +------ |              |
|                 | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

要访问单个帧中的字节范围,只需解压缩 对应的压缩帧:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |xxxxxxxxxxxxxx|
|  xxxxxxxx       |           |xxxxx+--------|
|                 |           +-----+        |
+-----------------+   +------ |              |
|                 | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

涵盖多个解压缩帧的范围需要解压缩 多个压缩帧:

Decompressed Space            Compressed Space
+-----------------+           +--------------+
|                 | <-------- |xxxxxxxxxxxxxx|
|                 |           |xxxxx+--------|
|              xxx|           +-----+xxxxxxxx|
+-----------------+   +------ |xxxxxxxxxxxxxx|
|xxxx             | <-+       +--------------+
|                 |       +-- |              |
|                 |       |   +--------------=
+-----------------+       |
|                 | <-----+
|       +---------|
+-------+

在 BlobFS 中使用

在 BlobFS 中,文件使用分块压缩库进行压缩, 实现随机访问和需求分页。

目前,只有在启用需求分页时才能使用随机访问。有需求来源 分页功能已停用,BlobFS 将首先加载和解压缩整个文件 在内存中有文件句柄时,在内存中缓冲文件。

启用需求分页后,BlobFS 会按原样延迟加载文件的某些部分 。BlobFS 将自己注册为 使用 zx_pager_create 系统调用。当 在 VMO 中访问不存在的页面时,会发生页面错误,这导致 BlobFS 标识名。

BlobFS 会查找包含目标网页的解压缩帧,以及 解压缩每一帧。解压每一帧后,验证数据 并通过 zx_pager_supply_pages 系统调用。