本文是深入研究 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