Squashfs 4.0 文件系统

Squashfs 是一种用于 Linux 的压缩只读文件系统。

它使用 zlib、lz4、lzo 或 xz 压缩来压缩文件、索引节点和目录。系统中的索引节点非常小,所有块都经过打包以尽量减少数据开销。支持大于 4K 的块大小,最大可达 1M 字节(默认块大小为 128K)。

Squashfs 适用于通用的只读文件系统用途、存档用途(例如,在可以使用 .tar.gz 文件的情况下)以及需要低开销的受限块设备/内存系统(例如,嵌入式系统)。

邮件列表: squashfs-devel@lists.sourceforge.net 网站:www.squashfs.org

1. 文件系统特性

Squashfs 文件系统特性与 Cramfs 的比较

最大文件系统大小

2^64

256 MiB

最大文件大小

~ 2 TiB

16 MiB

最大文件数

无限制

无限制

最大目录数

无限制

无限制

每个目录的最大条目数

无限制

无限制

最大块大小

1 MiB

4 KiB

元数据压缩

目录索引

稀疏文件支持

尾部打包(片段)

可导出(NFS 等)

硬链接支持

readdir 中的“.”和“..”

实际索引节点号

32 位 uids/gids

文件创建时间

Xattr 支持

ACL 支持

Squashfs 压缩数据、索引节点和目录。此外,索引节点和目录数据经过高度压缩,并以字节边界打包。每个压缩的索引节点的平均长度为 8 个字节(确切长度取决于文件类型,即常规文件、目录、符号链接和块/字符设备索引节点具有不同的大小)。

2. 使用 Squashfs

由于 squashfs 是只读文件系统,因此必须使用 mksquashfs 程序来创建已填充的 squashfs 文件系统。可以从 http://www.squashfs.org 获取此程序和其他 squashfs 实用程序。也可以从此站点获取使用说明。

squashfs-tools 开发树现在位于 kernel.org 上

git://git.kernel.org/pub/scm/fs/squashfs/squashfs-tools.git

2.1 挂载选项

errors=%s

指定 squashfs 错误是否触发内核崩溃

continue

错误不触发崩溃(默认)

panic

遇到错误时触发崩溃,类似于其他几个文件系统(例如,btrfs、ext4、f2fs、GFS2、jfs、ntfs、ubifs)

这允许保存内核转储,有助于分析和调试损坏。

threads=%s

选择解压缩模式或线程数

如果设置了 SQUASHFS_CHOICE_DECOMP_BY_MOUNT

single

使用单线程解压缩(默认)

任何时候只能解压缩一个块(数据或元数据)。这会将 CPU 和内存使用量限制在最低限度,但由于等待解压缩器的可用性,当使用多个 CPU 机器时,它在并行 I/O 工作负载上的性能也很差。

multi

每个核心最多使用两个并行解压缩器

如果您有并行 I/O 工作负载,并且您的系统有足够的内存,则使用此选项可能会提高整体 I/O 性能。它会根据需求动态分配解压缩器。

percpu

每个核心最多使用一个解压缩器

它使用 percpu 变量来确保解压缩在各个核心之间负载均衡。

1|2|3|...

配置用于解压缩的线程数

上限为 num_online_cpus() * 2。

如果 **未** 设置 SQUASHFS_CHOICE_DECOMP_BY_MOUNT 且同时设置了 SQUASHFS_DECOMP_MULTI 和 SQUASHFS_MOUNT_DECOMP_THREADS

2|3|...

配置用于解压缩的线程数

上限为 num_online_cpus() * 2。

3. Squashfs 文件系统设计

squashfs 文件系统最多由九个部分组成,这些部分以字节对齐方式打包在一起

 ---------------
|  superblock   |
|---------------|
|  compression  |
|    options    |
|---------------|
|  datablocks   |
|  & fragments  |
|---------------|
|  inode table  |
|---------------|
|   directory   |
|     table     |
|---------------|
|   fragment    |
|    table      |
|---------------|
|    export     |
|    table      |
|---------------|
|    uid/gid    |
|  lookup table |
|---------------|
|     xattr     |
|     table     |
 ---------------

在从源目录读取文件时,压缩的数据块会写入文件系统,并检查是否有重复项。写入所有文件数据后,将写入已完成的索引节点、目录、片段、导出、uid/gid 查找和 xattr 表。

3.1 压缩选项

压缩器可以选择性地支持特定于压缩的选项(例如,字典大小)。如果使用了非默认的压缩选项,则会将其存储在此处。

3.2 索引节点

元数据(索引节点和目录)以 8K 字节的块进行压缩。每个压缩的块都以两个字节的长度为前缀,如果该块未压缩,则设置最高位。如果设置了 -noI 选项,或者压缩块大于未压缩的块,则该块将不会被压缩。

索引节点会打包到元数据块中,并且不会与块边界对齐,因此索引节点会重叠压缩块。索引节点由 48 位数字标识,该数字编码包含索引节点的压缩元数据块的位置,以及索引节点所在块的字节偏移量(<块,偏移量>)。

为了最大程度地提高压缩率,每种文件类型(常规文件、目录、设备等)都有不同的索引节点,索引节点的内容和长度会随类型而变化。

为了进一步最大程度地提高压缩率,定义了两种类型的常规文件索引节点和目录索引节点:针对频繁出现的常规文件和目录进行优化的索引节点,以及必须存储额外信息的扩展类型。

3.3 目录

与索引节点类似,目录打包到压缩的元数据块中,存储在目录表中。使用包含目录的元块的起始地址和解压缩块的偏移量(<块,偏移量>)访问目录。

目录以稍微复杂的方式组织,而不仅仅是文件名列表。这种组织利用了以下事实:(在大多数情况下)文件的索引节点将位于同一压缩的元数据块中,因此可以共享起始块。因此,目录以两级列表的形式组织,目录标头包含共享的起始块值,以及一系列目录条目,每个条目都共享共享的起始块。一旦/如果索引节点起始块发生变化,则会写入新的目录标头。目录标头/目录条目列表会重复多次,直到必要为止。

目录经过排序,并且可以包含目录索引以加快文件查找速度。目录索引存储每个元块的一个条目,每个条目存储索引/文件名映射到每个元数据块中的第一个目录标头。目录按字母顺序排序,在查找时,线性扫描索引,查找字母顺序大于正在查找的文件名的第一个文件名。此时,已找到文件名所在的元数据块的位置。索引的总体思路是确保只需解压缩一个元数据块即可进行查找,而与目录的长度无关。此方案的优点是不需要额外的内存开销,并且不需要在磁盘上占用太多额外的存储空间。

3.4 文件数据

常规文件由一系列连续的压缩块和/或压缩的片段块(尾部打包块)组成。每个数据块的压缩大小都存储在文件索引节点中包含的块列表中。

为了加快读取“大型”文件(256 MB 或更大)时对数据块的访问速度,代码实现了一个索引缓存,该缓存缓存从块索引到磁盘上的数据块位置的映射。

索引缓存允许 Squashfs 处理大文件(高达 1.75 TiB),同时在磁盘上保留简单且节省空间的块列表。缓存分为多个槽,最多缓存八个 224 GiB 文件(128 KiB 块)。较大的文件使用多个槽,1.75 TiB 文件使用所有 8 个槽。索引缓存旨在节省内存,默认情况下使用 16 KiB。

3.5 片段查找表

常规文件可以包含片段索引,该索引使用片段查找表映射到磁盘上的片段位置和压缩大小。此片段查找表本身被压缩存储到元数据块中。使用第二个索引表来定位这些块。为了提高访问速度(并且因为它们很小),在挂载时读取此第二个索引表并将其缓存在内存中。

3.6 Uid/gid 查找表

为了节省空间,常规文件存储 uid 和 gid 索引,这些索引使用 id 查找表转换为 32 位 uids/gids。此表被压缩存储到元数据块中。使用第二个索引表来定位这些块。为了提高访问速度(并且因为它们很小),在挂载时读取此第二个索引表并将其缓存在内存中。

3.7 导出表

为了使 Squashfs 文件系统可导出(通过 NFS 等),文件系统可以选择性地(使用 -no-exports Mksquashfs 选项禁用)包含索引节点号到索引节点磁盘位置查找表。当导出代码重新实例化过期/刷新索引节点时,需要使用此表来使 Squashfs 将文件句柄中传递的索引节点号映射到磁盘上的索引节点位置。

此表以压缩形式存储在元数据块中。使用第二个索引表来定位这些块。为了提高访问速度(并且因为它很小),这个第二个索引表在挂载时读取并缓存在内存中。

3.8 扩展属性表

扩展属性表包含每个 inode 的扩展属性。每个 inode 的扩展属性存储在一个列表中,每个列表条目包含一个类型、名称和值字段。类型字段编码了扩展属性前缀(“user.”、“trusted.” 等),并且还编码了如何解释名称/值字段。目前,类型指示值是内联存储(在这种情况下,值字段包含扩展属性值),还是离线存储(在这种情况下,值字段存储对实际值存储位置的引用)。这允许将大值离线存储,从而提高扫描和查找性能,并且还允许对值进行去重,该值存储一次,所有其他出现的位置都保存对该值的离线引用。

扩展属性列表被打包到压缩的 8K 元数据块中。为了减少 inode 中的开销,不是在每个 inode 中存储扩展属性列表的磁盘位置,而是存储一个 32 位扩展属性 ID。这个扩展属性 ID 使用第二个扩展属性 ID 查找表映射到扩展属性列表的位置。

4. 待办事项和未解决的问题

4.1 待办事项列表

实现 ACL 支持。

4.2 Squashfs 内部缓存

Squashfs 中的块是压缩的。为了避免重复解压缩最近访问的数据,Squashfs 使用两个小的元数据和片段缓存。

缓存不用于文件数据块,这些数据块以正常方式解压缩并缓存在页面缓存中。缓存用于临时缓存因元数据(即 inode 或目录)或片段访问而读取的片段和元数据块。因为元数据和片段被打包到一起(以获得更高的压缩率),所以读取特定的元数据或片段会检索已与它打包在一起的其他元数据/片段,由于局部性原理,这些元数据/片段在不久的将来可能会被读取。临时缓存它们确保它们在不久的将来可以被访问,而无需额外的读取和解压缩。

未来,这个内部缓存可能会被使用内核页面缓存的实现所取代。由于页面缓存以页面大小的单元运行,这可能会在锁定和相关的竞争条件方面引入额外的复杂性。