使用 Btrfs – 子卷

本文是深入研究 Btrfs 系列文章的一部分,Btrfs 是 Fedora 工作站和 Fedora 银蓝自 Fedora Linux 33。

如果您错过了它,请阅读本系列的上一篇文章:https://fedoramagazine.org/working-with-btrfs-general-concepts/

介绍

子卷允许将 Btrfs 文件系统分区为单独的子文件系统。 这意味着您可以从 Btrfs 文件系统挂载子卷,就好像它们是独立的文件系统一样。 此外,您还可以为 example,通过 qgroups 定义子卷可能占用的最大空间(我们将在本系列的另一篇文章中讨论),或者使用子卷专门包含或排除快照中的文件(我们也将在另一篇文章中讨论)本系列文章)。 每一个默认 Fedora 工作站和 Fedora Silverblue 安装以来 Fedora Linux 33 使用子卷。 在这篇文章中,我们将探讨它是如何工作的。

下面你会发现很多与子卷相关的例子。 如果您想继续学习,您必须有权访问某些 Btrfs 文件系统和根访问权限。 您可以通过以下命令验证您的 /home/ 目录是否为 Btrfs:

$ findmnt -no FSTYPE /home
btrfs

此命令将输出 /home/ 目录的文件系统名称。 如果它显示 btrfs,则表示一切就绪。 让我们创建一个新目录来执行一些实验:

$ mkdir ~/btrfs-subvolume-test
$ cd ~/btrfs-subvolume-test

在下面的文本中,您会在如上所示的框中找到许多命令输出。 在阅读/比较命令输出时请记住 盒子内容包裹在行尾. 这使得难以识别为了可读性而被分成多行的长行。 如有疑问,请尝试调整浏览器窗口的大小并查看文本的行为方式!

创建和使用子卷

我们可以使用以下命令创建 Btrfs 子卷:

$ sudo btrfs subvolume create first
Create subvolume './first'

当我们检查当前目录时,我们会看到它现在有一个名为 first 的新文件夹。 请注意以下输出中的第一个字符 d:

$ ls -l
total 0
drwxr-xr-x. 1 root root 0 Oct 15 18:09 first

我们可以像处理任何常规文件夹一样处理它:我们可以重命名它、移动它、在其中创建新文件和文件夹等。请注意,该文件夹属于 root,因此我们必须是 root 才能执行这些操作。

如果它既像文件夹又像文件夹,我们怎么知道它是否是 Btrfs 子卷? 我们可以使用 btrfs 工具列出所有子卷:

$ sudo btrfs subvolume list .
ID 256 gen 30 top level 5 path home
ID 257 gen 30 top level 5 path root
ID 258 gen 25 top level 257 path root/var/lib/machines
ID 259 gen 29 top level 256 path hartan/btrfs-subvolume-test/first

如果您使用的是最新且未修改的 Fedora Linux 安装您可能会看到与上面相同的输出。 稍后我们将检查 home 和 root 以及所有数字的含义。 现在,我们看到在我们指定的路径上有一个子卷。 我们可以将输出限制在我们当前位置以下的子卷中:

$ sudo btrfs subvolume list -o .
ID 259 gen 29 top level 256 path home/hartan/btrfs-subvolume-test/first

让我们重命名子卷:

$ sudo mv first second
$ sudo btrfs subvolume list -o .
ID 259 gen 29 top level 256 path home/hartan/btrfs-subvolume-test/second

我们还可以嵌套子卷:

$ sudo btrfs subvolume create second/third
Create subvolume 'second/third'
$ sudo btrfs subvolume list .
ID 256 gen 34 top level 5 path home
ID 257 gen 37 top level 5 path root
ID 258 gen 25 top level 257 path root/var/lib/machines
ID 259 gen 37 top level 256 path hartan/btrfs-subvolume-test/second
ID 260 gen 37 top level 259 path hartan/btrfs-subvolume-test/second/third

我们还可以删除子卷,就像我们删除文件夹一样:

$ sudo rm -r second/third

或者通过特殊的 Btrfs 命令:

$ sudo btrfs subvolume delete second
Delete subvolume (no-commit): '/home/hartan/btrfs-subvolume-test/second'

像处理单独的文件系统一样处理 Btrfs 子卷

介绍中提到 Btrfs 子卷就像单独的文件系统一样。 这意味着我们可以挂载子卷并将一些挂载选项传递给它们。 首先,我们将创建一个小文件夹结构,以便更好地了解发生了什么:

$ mkdir -p a a/1 a/1/b
$ sudo btrfs subvolume create a/2
Create subvolume 'a/2'
$ sudo touch a/1/c a/1/b/d a/2/e

这是结构的样子:

$ tree
.
└── a
    ├── 1
    │   ├── b
    │   │   └── d
    │   └── c
    └── 2
        └── e

4 directories, 3 files

确认现在有一个新的 Btrfs 子卷:

$ sudo btrfs subvolume list -o .
ID 261 gen 41 top level 256 path home/hartan/btrfs-subvolume-test/a/2

要挂载子卷,我们必须知道 Btrfs 文件系统子卷所在的块设备的路径。 下面的命令告诉我们:

$ findmnt -vno SOURCE /home/
/dev/vda3

现在我们可以挂载子卷了。 确保将参数替换为适用于您的 PC 的值:

$ sudo mount -o subvol=home/hartan/btrfs-subvolume-test/a/2 /dev/vda3 a/1/b

请注意,我们使用 -o 标志为挂载程序提供额外的选项。 在这种情况下,我们告诉它从设备 /dev/vda3 上的 btrfs 文件系统挂载名为 home/hartan/btrfs-subvolume-test/a/2 的子卷。 这是一个特定于 Btrfs 的选项,在其他文件系统中不可用。

我们看到目录结构发生了变化:

$ tree
.
└── a
    ├── 1
    │   ├── b
    │   │   └── e
    │   └── c
    └── 2
        └── e

4 directories, 3 files

请注意,文件 e 现在存在两次,而 d 已经不存在了。 我们现在可以通过两条不同的路径访问同一个 Btrfs 子卷。 我们在任一路径中执行的所有更改都会立即反映在所有其他位置:

$ sudo touch a/1/b/x
$ ls -lA a/2
total 0
-rw-r--r--. 1 root root 0 Oct 15 18:14 e
-rw-r--r--. 1 root root 0 Oct 15 18:16 x

让我们更多地使用挂载选项。 为了 example 我们可以像这样在 a/1/b 下以只读方式挂载子卷(为您的 PC 插入参数!):

$ sudo umount a/1/b
$ sudo mount -o subvol=home/hartan/btrfs-subvolume-test/a/2,ro /dev/vda3 a/1/b

我们使用与上面相同的命令,只是我们在末尾添加了 ro。 现在我们不能再通过这个挂载创建文件了:

$ sudo touch a/1/b/y
touch: cannot touch 'a/1/b/y': Read-only file system

但直接访问子卷仍然像以前一样工作:

$ sudo touch a/2/y
$ tree
.
└── a
    ├── 1
    │   ├── b
    │   │   ├── e
    │   │   ├── x
    │   │   └── y
    │   └── c
    └── 2
        ├── e
        ├── x
        └── y

4 directories, 7 files

在我们继续之前不要忘记清理:

$ sudo rm -rf a
rm: cannot remove 'a/1/b/e': Read-only file system
rm: cannot remove 'a/1/b/x': Read-only file system
rm: cannot remove 'a/1/b/y': Read-only file system

哦不,发生了什么事? 好吧,由于我们在上面以只读方式安装了子卷,因此我们无法删除它。 从文件系统的角度来看,删除是一个写操作:删除 a/1/b/e, 我们删除目录条目 电子 从其父目录的目录内容, a/1/b 在这种情况下。 换句话说,我们必须写信给 a/1/b 告诉它 电子 不存在了。 所以首先我们再次卸载子卷,然后我们删除文件夹:

$ sudo umount a/1/b
$ sudo rm -rf a
$ tree
.

0 directories, 0 files

子卷 ID

还记得 subvolume list 子命令的第一个输出吗? 其中包含很多数字,所以让我们看看这到底是怎么回事。 我在这里复制了输出再看一眼:

ID 256 gen 30 top level 5 path home
ID 257 gen 30 top level 5 path root
ID 258 gen 25 top level 257 path root/var/lib/machines
ID 259 gen 29 top level 256 path hartan/btrfs-subvolume-test/first

我们看到一共有三列数字,每列前面都有几个字母来描述它们的作用。 第一列数字是子卷 ID。 子卷 ID 在 Btrfs 文件系统中是唯一的,因此可以唯一标识子卷。 这意味着名为 home 的子卷也可以通过其 ID 引用 256. 在上面的挂载命令中我们写道:

$ sudo mount -o subvol=hartan/...

另一个完全合法的选择是使用子卷 ID:

$ sudo mount -o subvolid=...

子卷 ID 开始于 256 并为每个创建的子卷增加 1。 但是有一个例外:文件系统根始终具有子卷名称 / 和子卷 ID 5。没错,即使 Btrfs 文件系统的根在技术上也是一个子卷。 这只是隐式已知的,因此它不会出现在 btrfs 子卷列表的输出中。 如果您在没有 subvol 或 subvolid 参数的情况下挂载 Btrfs 文件系统,则 subvolid=5 的根子卷被假定为默认值。 下面我们将看到一个 example 当一个人可能想要显式挂载文件系统根目录时。

第二列数字是生成计数器,并在每次 Btrfs 交易时递增。 这主要是一个内部计数器,这里不会进一步讨论。

最后,第三列数字是父子卷的子卷 ID。 在上面的输出中,我们看到子卷 home 和 root 都有 5 作为它们的父子卷 ID。 请记住,ID 5 具有特殊含义:它是文件系统根。 所以我们知道 home 和 root 是根子卷的孩子。 另一方面,hartan/btrfs-subvolume-test/first 是 ID 为 256 的子卷的子卷,在我们的例子中是 home。

在下一节中,我们将了解子卷 root 和 home 的来源。

检查默认子卷 Fedora Linux

当您从头开始创建新的 Btrfs 文件系统时,其中将没有子卷(当然根子卷除外)。 那么 home 和 root 子卷在哪里 Fedora Linux从何而来?

这些是由安装程序在安装时创建的。 传统安装通常会为 / 和 /home 目录包含一个单独的文件系统分区。 在引导过程中,这些然后被适当地安装以组装一个完整的文件系统。 但是这种方法有一个问题:除非您使用 lvm 等技术,否则很难在将来的某个时候更改分区大小。 因此,您最终可能会遇到这样的情况,即您的 / 或 /home 空间不足,而相应的其他分区还有大量未使用的可用空间。

由于 Btrfs 子卷都是同一文件系统的一部分,因此它们将共享底层文件系统提供的空间。 还记得我们在上面创建子卷吗? 我们从未告诉 Btrfs 它们有多大:子卷可以占用文件系统的所有空间,默认情况下没有什么可以阻止它这样做。 但是,我们可以通过 Btrfs qgroups 动态施加大小限制,这也可以在运行时进行修改(我们将在本系列的后续文章中看到如何修改)。

将 / 和 /home 分开的另一个好处是我们可以单独拍摄快照。 子卷是快照的边界,快照永远不会包含拍摄快照的子卷下的其他子卷的内容。 有关快照的更多详细信息,请参阅本系列的下一篇文章。

足够的理论! 让我们看看这是怎么回事。 首先确保您的根文件系统实际上是 Btrfs 类型:

$ findmnt -no FSTYPE /
btrfs

然后获取它所在的分区:

$ findmnt -vno SOURCE /
/dev/vda3

请记住,我们可以通过其特殊的子卷 ID 5 挂载文件系统根目录(适应文件系统分区!):

$ mkdir fedora-rootsubvol
$ sudo mount -o subvolid=5 /dev/vda3 ./fedora-rootsubvol
$ ls fedora-rootsubvol/
home  root

还有我们的子卷 Fedora Linux安装! 但是如何 Fedora linux知道子卷root属于/,home属于/home?

文件 /etc/fstab 包含关于文件系统的所谓静态信息。 简单来说,在引导系统期间,逐行读取该文件,并挂载那里列出的所有文件系统。 在我的系统上,该文件如下所示:

$ cat /etc/fstab
# [ ... ]
# /etc/fstab
# Created by anaconda on Sat Oct 15 12:01:57 2022
# [ ... ]
#
UUID=5e4e42bb-4f2f-4f0e-895f-d1a46ea47807 /                       btrfs   subvol=root,compress=zstd:1 0 0
UUID=e3a798a8-b8f2-40ca-9da7-5e292a6412aa /boot                   ext4    defaults        1 2
UUID=5e4e42bb-4f2f-4f0e-895f-d1a46ea47807 /home                   btrfs   subvol=home,compress=zstd:1 0 0

(请注意,上面的“UUID”行已被换成两行)

每行开头的 UUID 只是一种识别系统中磁盘和文件系统分区的方法(大致相当于我上面使用的 /dev/vda3)。 第二列是文件系统树中应该挂载该文件系统的路径。 第三列是文件系统类型。 我们看到 / 和 /home 的条目是 btrfs 类型,这正是我们所期望的! 最后,在第四列中我们看到了神奇之处:这些是挂载选项,其中显示要使用选项 subvol=root 挂载 /。 这正是我们一直在 btrfs subvolume list / 的输出中看到的子卷!

有了这些信息,我们可以重建创建这个文件系统条目的 mount 调用:

$ sudo mount -o subvol=root,compress=zstd:1 UUID=5e4e42bb-4f2f-4f0e-895f-d1a46ea47807 /
(again, the line above has been wrapped into two)

就是这样 Fedora Linux 使用 Btrfs 子卷! 如果你好奇为什么 Fedora Linux 决定使用 Btrfs 作为默认文件系统,请参考下面链接的更改建议 [1].

有关 Btrfs 子卷的更多信息

Btrfs wiki 提供了有关子卷的更多信息,最重要的是有关可应用于 Btrfs 子卷的挂载选项的信息。 某些选项(如压缩)只能应用于文件系统级别,因此会影响 Btrfs 文件系统的所有子卷。 您可以找到下面链接的条目 [2].

如果您发现很难区分哪些目录是普通目录,哪些是子卷,您可以随意为您的子卷采用特殊的命名约定。 为了 example,您可以在子卷名称前加上“@”前缀,以便于区分它们。

既然您知道子卷的行为类似于文件系统,那么您可能会问如何最好地将子卷放置在特定位置。 假设您想要在 ~/games 下创建一个 Btrfs 子卷,您的主目录 (~) 本身就是一个子卷,您如何实现? 鉴于 example 上面,你可以使用像这样的命令 sudo btrfs 子卷创建 ~/games。 这样,您就创建了所谓的嵌套子卷:在您的子卷内~,现在有一个子卷游戏。 这是处理这种情况的完美方式。

另一个有效的解决方案是做什么 Fedora 默认情况下:在根子卷下创建所有子卷(即它们的父子卷 ID 为 5),并将它们安装到适当的位置。 Btrfs wiki 概述了这些方法,并简要讨论了它们各自对文件系统管理的影响 [5].

结论

在本文中,我们发现了 Btrfs 子卷,它们的作用类似于 Btrfs 文件系统内的独立 Btrfs 文件系统。 我们学习了如何创建、装载和删除子卷。 最后,我们探讨了如何 Fedora Linux 使用了子卷——我们根本没有注意到。

本系列的下一篇文章将涉及:

  • 快照——时光倒流
  • 压缩——透明节省存储空间
  • Qgroups——限制你的文件系统大小
  • RAID – 替换您的 mdadm 配置

如果您想了解更多与 Btrfs 相关的其他主题,请查看 Btrfs Wiki [3] 和文档 [4]. 如果您还没有阅读本系列的第一篇文章,请不要忘记阅读! 如果您觉得本系列文章中缺少某些内容,请在下面的评论中告诉我们。 下篇文章见!

来源

[1]: https://fedoraproject.org/wiki/Changes/BtrfsByDefault#Benefit_to_Fedora
[2]: https://btrfs.readthedocs.io/en/latest/Subvolumes.html
[3]: https://btrfs.wiki.kernel.org/index.php/Main_Page
[4]: https://btrfs.readthedocs.io/en/latest/Introduction.html
[5]: https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Layout