Btrfs 詳解:子卷

以防你忘記,這是系列文章中的前一篇:Btrfs 詳解:基礎(chǔ)概念。
簡介
子卷Subvolume 允許將一個 Btrfs 文件系統(tǒng)劃分成多個獨立的子文件系統(tǒng)。這意味著你可以從 Btrfs 文件系統(tǒng)掛載子卷,就好像它們是獨立的文件系統(tǒng)。除此之外,例如,你還可以通過 限額組qgroup(我們將在本系列的另一篇文章里介紹)定義子卷能夠占據(jù)的最大空間,或者用子卷去包含或排除快照中的文件(我們會后面的文章中會講到)。自 Fedora Linux 33 后每個 Fedora Workstation 和 Fedora Silverblue 默認安裝過程中會利用子卷。在這篇文章中我們會介紹它是如何工作的。
下面你會找到很多關(guān)于子卷的例子。如果你想跟著操作,你必須擁有訪問某些 Btrfs 文件系統(tǒng)的權(quán)限和 root 權(quán)限。你可以通過下面命令來驗證你的 /home/ 目錄是否是 Btrfs 。
$ findmnt -no FSTYPE /home
btrfs這個命令會輸出你 /home/ 目錄的文件系統(tǒng)名。如果它是 btrfs,那就可以了。讓我們創(chuàng)建一個新的目錄去做實驗:
$ mkdir ~/btrfs-subvolume-test
$ cd ~/btrfs-subvolume-test在下面的文本中,你會看到很多像上面顯示的那樣的命令輸出框。請在閱讀/比較命令輸出時請記住,框中的內(nèi)容在行末會被換行。這使得識別跨多行的長行變得困難,降低了可讀性。如果有疑問,試著調(diào)整瀏覽器窗口的大小,看看文本的變化!
創(chuàng)建和使用子卷
我們可以通過以下命令創(chuàng)建一個 Btrfs 子卷:
$ sudo btrfs subvolume create first
Create subvolume './first'當(dāng)我們檢查當(dāng)前目錄,我們可以看到現(xiàn)在有一個名為 first 的新目錄。注意到下面輸出的第一個字符 d:
$ ls -l
total 0
drwxr-xr-x. 1 root root 0 Oct 15 18:09 first我們可以像常規(guī)目錄一樣操作它:我們可以重命名它,移動它,在里面創(chuàng)建新文件和目錄,等等。注意到目錄屬于 root,所以我們必須以 root 身份去做這些事情。
如果它表現(xiàn)和看起來就像個目錄,那我們?nèi)绾沃肋@是不是一個 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 ,還有全部數(shù)字的含義?,F(xiàn)在,我們看到在我們指定的路徑下有一個子卷。我們可以將輸出限制在我們當(dāng)前位置下面的子卷:
$ 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'像單獨的文件系統(tǒng)一樣操作子卷
前面的簡介里說 Btrfs 子卷就好像單獨的文件系統(tǒng)。這意味著我們可以掛載子卷并且傳遞一些掛載選項給它。我們先創(chuàng)建一個小的目錄結(jié)構(gòu)去更好的理解發(fā)生了什么:
$ 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這就是目錄結(jié)構(gòu)的樣子:
$ tree
.
└── a
├── 1
│ ├── b
│ │ └── d
│ └── c
└── 2
└── e
4 directories, 3 files驗證現(xiàn)在這里有一個新的 Btrfs 子卷:
$ sudo btrfs subvolume list -o .
ID 261 gen 41 top level 256 path home/hartan/btrfs-subvolume-test/a/2為了掛載子卷,我們必須知道 Btrfs 子卷所在的塊設(shè)備路徑。下面的命令會告訴我們:
$ findmnt -vno SOURCE /home/
/dev/vda3現(xiàn)在我們掛載子卷。確保你將參數(shù)替換成你 PC 上的:
$ sudo mount -o subvol=home/hartan/btrfs-subvolume-test/a/2 /dev/vda3 a/1/b觀察到我們使用 -o 參數(shù)去提供額外的選項去掛載程序。在這里我們告訴它掛載在設(shè)備 /dev/vda3 上 btrfs 文件系統(tǒng)里名為 home/hartan/btrfs-subvolume-test/a/2 的子卷。這是 Btrfs 特有的選項,在其他文件系統(tǒng)里沒有的。
我們可以看到目錄結(jié)構(gòu)變化了:
$ tree
.
└── a
├── 1
│ ├── b
│ │ └── e
│ └── c
└── 2
└── e
4 directories, 3 files現(xiàn)在文件 e 出現(xiàn)了兩次, d 不見了。我們現(xiàn)在可以用兩個不同的路徑訪問相同的 Btrfs 子卷。在一個路徑的所有變化會被立刻反應(yīng)在其他的位置:
$ 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 的參數(shù)):
$ sudo umount a/1/b
$ sudo mount -o subvol=home/hartan/btrfs-subvolume-test/a/2,ro /dev/vda3 a/1/b我們和上面使用相同的命令,除了我們加上了 ro 在末尾?,F(xiàn)在我們不能在這個掛載點上創(chuàng)建文件:
$ 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天啊,發(fā)生了什么?噢,因為我們在上面掛載只讀子卷,所以不能刪除它。從文件系統(tǒng)的角度來看,刪除是一種寫入操作:為了刪除 a/2/b/e,我們從父目錄 a/1/b 的內(nèi)容中刪除目錄項 e。換句話來說,我們必須 寫入 a/1/b 去表明 e 不復(fù)存在。所以我們先卸載子卷,然后移除目錄:
$ sudo umount a/1/b
$ sudo rm -rf a
$ tree
.
0 directories, 0 files子卷 ID
還記得 btrfs subvolume list 命令的第一次輸出嗎?那包含了很多數(shù)字,讓我們看看這些究竟什么。我在這里復(fù)制了輸出,以便再次查看:
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我們看到有三列數(shù)字,每個前面有一些字母來描述它們的作用。第一列是子卷 ID 。子卷 ID 在 Btrfs 文件系統(tǒng)是唯一的,而且唯一地標(biāo)識子卷。這意味著名為 home 的子卷也可以用它的 ID 256 來引用。之前的掛載命令是這樣寫的:
$ sudo mount -o subvol=hartan/...另外一個完全合法的選擇是使用子卷 ID :
$ sudo mount -o subvolid=...子卷 ID 從 256 開始,每創(chuàng)建一個子卷依次遞增 1 。但是在這里有一個例外:文件系統(tǒng)的根的子卷名稱總是為 /,并且子卷 ID 是 5 。沒錯,即使文件系統(tǒng)的根技術(shù)上也是一個子卷。這是不言而喻的,因此不會出現(xiàn)在 btrfs subvolume 的輸出列表里。如果你沒有用 subvol 和 subvolid 參數(shù)去掛載一個 Btrfs 文件系統(tǒng),subvolid=5 的頂級子卷就是默認的掛載對象。下面我們會看到一個想要顯式掛載文件系統(tǒng)根的例子。
第二列的數(shù)字是生成號,并且在每次 Btrfs 事務(wù)中遞增。這幾乎是一個內(nèi)部的計數(shù)器,我們不會在這里討論。
最后,第三列數(shù)字是 父 子卷的子卷 ID。在上面的輸出我們可以看到子卷 home 和 root 的父子卷 ID 都是 5。記住 ID 5 的特殊含義:這是文件系統(tǒng)的根。所以我們知道 home 和 root 都是頂級子卷的子卷。另一方面 hartan/btrfs-subvolume-test.first 是子卷 ID 256(也就是 home)的子卷。
在下一節(jié)我們會看看子卷 root 和 home 是怎么來的。
檢查 Fedora Linux 的默認子卷
當(dāng)你從頭創(chuàng)建一個新的 Btrfs 文件系統(tǒng),里面是沒有子卷的(當(dāng)然,除了頂級子卷)。所以 Fedora Linux 里的 home 和 root 子卷是哪里來的?
它們是安裝程序在安裝時創(chuàng)建的。傳統(tǒng)的安裝經(jīng)常會為 / 和 /home 目錄包含單獨的文件系統(tǒng)分區(qū)。在啟動時,它們通過恰當(dāng)?shù)膾燧d組成一個完整的文件系統(tǒng)。但這個方法有一個問題:除非你使用像 lvm 這樣的技術(shù),想在將來改變分區(qū)的大小是非常難的。因而你可能出現(xiàn) / 或 /home 用完空間的情況,然而還有很多其他沒被使用的分區(qū)和空間剩余。
因為 Btrfs 子卷全都是相同文件系統(tǒng)的一部分,它們共享底層文件系統(tǒng)提供的空間。還記得我們在上面創(chuàng)建的子卷嗎?我們從未告訴 Btrfs 它們多大:一個子卷可以占據(jù)文件系統(tǒng)擁有的全部空間,默認是不會阻止這種行為的。但是,我們 可以 通過 Btrfs 的 限額組qgroup
另外一個分離 / 和 /home 的優(yōu)勢是我們可以分別進行 快照
理論已經(jīng)足夠了!我們來看看這是怎么回事。首先確保你的根文件系統(tǒng)類型是 Btrfs :
$ findmnt -no FSTYPE /
btrfs然后我們獲取它所在的分區(qū):
$ findmnt -vno SOURCE /
/dev/vda3記住我們可以通過特殊的子卷 ID 5 掛載文件系統(tǒng)的根(適應(yīng)文件系統(tǒng)分區(qū)?。?/p>
$ 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 包含了所謂的文件系統(tǒng)的靜態(tài)信息。簡而言之,在你系統(tǒng)啟動的時候會一行一行地讀取這個文件,然后掛載那里列出的所有文件系統(tǒng)。在我的系統(tǒng)上,這個文件長這樣:
$ 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” 開頭行的內(nèi)容被換行成兩行)
每行開頭的 UUID 用于標(biāo)識你系統(tǒng)上的硬盤和文件系統(tǒng)分區(qū)(大概相當(dāng)于我在上面使用的 /dev/vda3 )。第二列是文件系統(tǒng)應(yīng)該掛載在文件系統(tǒng)樹上的路徑。第三列是文件系統(tǒng)類型。我們可以看到 / 和 /home 都是 btrfs 類型,正如我們期望的那樣!最后,第四列是:這些是掛載選項,這里說通過 subvol=root 選項去掛載 / 。這正是我們一直在 btrfs subvolume list / 里看到的輸出!
有了這些信息,我們可以重新構(gòu)建創(chuàng)建這個文件系統(tǒng)項的 mount 命令
$ sudo mount -o subvol=root,compress=zstd:1 UUID=5e4e42bb-4f2f-4f0e-895f-d1a46ea47807 /(再次,上面的 “UUID” 開頭行的內(nèi)容被換行成兩行)
這就是 Fedora Linux 如何使用 Btrfs 子卷!如果你對好奇 Fedora Linux 為什么選擇 Btrfs 作為默認的文件系統(tǒng),請參閱下面鏈接的更改提議 [1]。
Btrfs 子卷的更多內(nèi)容
Btrfs 維基提供了關(guān)于子卷的更多信息,其中最重要的是可應(yīng)用于 Btrfs 子卷的掛載選項。有些選項,比如 compress 只能應(yīng)用到文件系統(tǒng)的層面,因而會影響一個 Btrfs 文件系統(tǒng)的所有子卷。你可以通過下面的鏈接找到entry [2]。
如果你對哪些目錄是普通目錄和哪些是子卷有困惑,你可以對你的子卷采用特殊的命名約定。例如,你可以給子卷名加上 @ 前綴去方便區(qū)分。
現(xiàn)在你知道子卷表現(xiàn)得就像文件系統(tǒng),有人可能會問如何才能最好地將子卷放置在特定位置。比如你想要一個 Btrfs 子卷在 ~/games 下面,然而你的主目錄(~)本身就是一個子卷,你該如何實現(xiàn)呢?鑒于上面的例子,你可以使用像 sudo btrfs subvolume create ~/games 的命令。這樣,你創(chuàng)建了所謂的 嵌套 子卷:在你的子卷 ~ 里,有一個子卷 games 。這正是一種達成目的的方法。
其他有效的方法就是如同 Fedora 默認行為那樣:在根子卷下創(chuàng)建所有子卷(也就是它們的父子卷 ID 是 5 ),然后掛載它們到特定的位置。Btrfs 維基有這些方法的概述和對于各自文件系統(tǒng)管理影響的簡短討論 [3]。
總結(jié)
在本文中,我們探索了 Btrfs 子卷,它們像是 Btrfs 文件系統(tǒng)內(nèi)部的獨立的 Btrfs 文件系統(tǒng)。我們學(xué)習(xí)了如何創(chuàng)建、掛載和刪除子卷。最后,我們探討了 Fedora Linux 如何在我們完全沒有注意到的情況下使用子卷。
本系列的下一篇文章將討論:
- 快照 - 回到過去
- 壓縮 - 透明地節(jié)省存儲空間
- 配額組 - 限制文件系統(tǒng)大小
- RAID - 替代 mdadm 配置
如果你還想了解與 Btrfs 相關(guān)的其他主題,請查看 Btrfs 維基 [4] 和文檔 [5]。不要忘記查看本系列的第一篇文章(如果你還沒有看過的話)!如果你認為本系列文章缺少了一些內(nèi)容,請在下面的評論中告訴我們。再會!
參考資料
- https://fedoraproject.org/wiki/Changes/BtrfsByDefault#Benefit_to_Fedora ??
- https://btrfs.readthedocs.io/en/latest/Subvolumes.html ??
- https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Layout ??
- https://btrfs.wiki.kernel.org/index.php/Main_Page ??
- https://btrfs.readthedocs.io/en/latest/Introduction.html ??




















