Btrfs 详解:子卷
创始人
2024-03-02 17:32:29
0

这篇文章是《Btrfs 详解》系列文章中的一篇。从 Fedora Linux 33 开始,Btrfs 就是 Fedora Workstation 和 Fedora Silverblue 的默认文件系统。

以防你忘记,这是系列文章中的前一篇:Btrfs 详解:基础概念

简介

子卷 Subvolume 允许将一个 Btrfs 文件系统划分成多个独立的子文件系统。这意味着你可以从 Btrfs 文件系统挂载子卷,就好像它们是独立的文件系统。除此之外,例如,你还可以通过 限额组 qgroup (我们将在本系列的另一篇文章里介绍)定义子卷能够占据的最大空间,或者用子卷去包含或排除快照中的文件(我们会后面的文章中会讲到)。自 Fedora Linux 33 后每个 Fedora Workstation 和 Fedora Silverblue 默认安装过程中会利用子卷。在这篇文章中我们会介绍它是如何工作的。

下面你会找到很多关于子卷的例子。如果你想跟着操作,你必须拥有访问某些 Btrfs 文件系统的权限和 root 权限。你可以通过下面命令来验证你的 /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,且未修改过,你很可能会看到和上面一样的输出。我们会在之后检查 homeroot ,还有全部数字的含义。现在,我们看到在我们指定的路径下有一个子卷。我们可以将输出限制在我们当前位置下面的子卷:

$ 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 子卷就好像单独的文件系统。这意味着我们可以挂载子卷并且传递一些挂载选项给它。我们先创建一个小的目录结构去更好的理解发生了什么:

$ 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

让我们尝试更多的挂载选项。例如我们可以像这样以只读方式挂载子卷到 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/2/b/e,我们从父目录 a/1/b 的内容中删除目录项 e。换句话来说,我们必须 写入 a/1/b 去表明 e 不复存在。所以我们先卸载子卷,然后移除目录:

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

0 directories, 0 files

子卷 ID

还记得 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

我们看到有三列数字,每个前面有一些字母来描述它们的作用。第一列是子卷 ID 。子卷 ID 在 Btrfs 文件系统是唯一的,而且唯一地标识子卷。这意味着名为 home 的子卷也可以用它的 ID 256 来引用。之前的挂载命令是这样写的:

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

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

$ sudo mount -o subvolid=...

子卷 ID 从 256 开始,每创建一个子卷依次递增 1 。但是在这里有一个例外:文件系统的根的子卷名称总是为 /,并且子卷 ID 是 5 。没错,即使文件系统的根技术上也是一个子卷。这是不言而喻的,因此不会出现在 btrfs subvolume 的输出列表里。如果你没有用 subvolsubvolid 参数去挂载一个 Btrfs 文件系统,subvolid=5 的顶级子卷就是默认的挂载对象。下面我们会看到一个想要显式挂载文件系统根的例子。

第二列的数字是生成号,并且在每次 Btrfs 事务中递增。这几乎是一个内部的计数器,我们不会在这里讨论。

最后,第三列数字是 子卷的子卷 ID。在上面的输出我们可以看到子卷 homeroot 的父子卷 ID 都是 5。记住 ID 5 的特殊含义:这是文件系统的根。所以我们知道 homeroot 都是顶级子卷的子卷。另一方面 hartan/btrfs-subvolume-test.first 是子卷 ID 256(也就是 home)的子卷。

在下一节我们会看看子卷 roothome 是怎么来的。

检查 Fedora Linux 的默认子卷

当你从头创建一个新的 Btrfs 文件系统,里面是没有子卷的(当然,除了顶级子卷)。所以 Fedora Linux 里的 homeroot 子卷是哪里来的?

它们是安装程序在安装时创建的。传统的安装经常会为 //home 目录包含单独的文件系统分区。在启动时,它们通过恰当的挂载组成一个完整的文件系统。但这个方法有一个问题:除非你使用像 lvm 这样的技术,想在将来改变分区的大小是非常难的。因而你可能出现 //home 用完空间的情况,然而还有很多其他没被使用的分区和空间剩余。

因为 Btrfs 子卷全都是相同文件系统的一部分,它们共享底层文件系统提供的空间。还记得我们在上面创建的子卷吗?我们从未告诉 Btrfs 它们多大:一个子卷可以占据文件系统拥有的全部空间,默认是不会阻止这种行为的。但是,我们 可以 通过 Btrfs 的 限额组 qgroup 动态地约束其大小,同时也可以在运行时修改(我们将在后续的文章中了解如何做的)。

另外一个分离 //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 /

(再次,上面的 “UUID” 开头行的内容被换行成两行)

这就是 Fedora Linux 如何使用 Btrfs 子卷!如果你对好奇 Fedora Linux 为什么选择 Btrfs 作为默认的文件系统,请参阅下面链接的更改提议 [1] 。

Btrfs 子卷的更多内容

Btrfs 维基提供了关于子卷的更多信息,其中最重要的是可应用于 Btrfs 子卷的挂载选项。有些选项,比如 compress 只能应用到文件系统的层面,因而会影响一个 Btrfs 文件系统的所有子卷。你可以通过下面的链接找到entry [2] 。

如果你对哪些目录是普通目录和哪些是子卷有困惑,你可以对你的子卷采用特殊的命名约定。例如,你可以给子卷名加上 @ 前缀去方便区分。

现在你知道子卷表现得就像文件系统,有人可能会问如何才能最好地将子卷放置在特定位置。比如你想要一个 Btrfs 子卷在 ~/games 下面,然而你的主目录(~)本身就是一个子卷,你该如何实现呢?鉴于上面的例子,你可以使用像 sudo btrfs subvolume create ~/games 的命令。这样,你创建了所谓的 嵌套 子卷:在你的子卷 ~ 里,有一个子卷 games 。这正是一种达成目的的方法。

其他有效的方法就是如同 Fedora 默认行为那样:在根子卷下创建所有子卷(也就是它们的父子卷 ID 是 5 ),然后挂载它们到特定的位置。Btrfs 维基有这些方法的概述和对于各自文件系统管理影响的简短讨论 [3] 。

总结

在本文中,我们探索了 Btrfs 子卷,它们像是 Btrfs 文件系统内部的独立的 Btrfs 文件系统。我们学习了如何创建、挂载和删除子卷。最后,我们探讨了 Fedora Linux 如何在我们完全没有注意到的情况下使用子卷。

本系列的下一篇文章将讨论:

  • 快照 - 回到过去
  • 压缩 - 透明地节省存储空间
  • 配额组 - 限制文件系统大小
  • RAID - 替代 mdadm 配置

如果你还想了解与 Btrfs 相关的其他主题,请查看 Btrfs 维基 [4] 和文档 [5] 。不要忘记查看本系列的第一篇文章(如果你还没有看过的话)!如果你认为本系列文章缺少了一些内容,请在下面的评论中告诉我们。再会!

参考资料

  1. https://fedoraproject.org/wiki/Changes/BtrfsByDefault ↩︎
  2. https://btrfs.readthedocs.io/en/latest/Subvolumes.html ↩︎
  3. https://btrfs.wiki.kernel.org/index.php/SysadminGuide ↩︎
  4. https://btrfs.wiki.kernel.org/index.php/Main_Page ↩︎
  5. https://btrfs.readthedocs.io/en/latest/Introduction.html ↩︎

(题图:MJ/f047ea87-2490-40e5-9f91-d48d236675e5)


via: https://fedoramagazine.org/working-with-btrfs-subvolumes/

作者:Andreas Hartmann 选题:lujun9972 译者:A2ureStone 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

Btrfs 详解:压缩
这篇文章将探索 Btrfs 中的透明文件系统压缩,以及它如何帮助节...
2024-03-02 17:42:43
Btrfs 详解:快照
这篇文章会探讨什么是 Btrfs 快照,它们如何工作的,你在日常生...
2024-03-02 17:40:37
使用 Btrfs 快照方便...
在 2018 年的一篇 早前的文章 中,我们介绍了在升级 Fedo...
2024-03-02 17:36:32
Btrfs 详解:子卷
这篇文章是《Btrfs 详解》系列文章中的一篇。从 Fedora ...
2024-03-02 17:32:29
Btrfs 详解:基础概念
这篇文章是《Btrfs 详解》系列文章中的一篇。从 Fedora ...
2024-03-02 17:17:53
如何将你的文件系统转换为 ...
引言这篇概述文章将告诉你为何以及如何迁移你的当前分区到 Btrfs...
2024-03-02 11:08:53

热门资讯

Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
使用 KRAWL 扫描 Kub... 用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。当你使用 Kubernet...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
通过 SaltStack 管理... 我在搜索Puppet的替代品时,偶然间碰到了Salt。我喜欢puppet,但是我又爱上Salt了:)...
Epic 游戏商店现在可在 S... 现在可以在 Steam Deck 上运行 Epic 游戏商店了,几乎无懈可击! 但是,它是非官方的。...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
2024 开年,LLUG 和你... Hi,Linuxer,2024 新年伊始,不知道你是否已经准备好迎接新的一年~ 2024 年,Lin...
什么是 KDE Connect... 什么是 KDE Connect?它的主要特性是什么?它应该如何安装?本文提供了基本的使用指南。科技日...
Opera 浏览器内置的 VP... 昨天我们报道过 Opera 浏览器内置了 VPN 服务,用户打开它可以防止他们的在线活动被窥视。不过...