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