Linux中國

Btrfs 詳解:子卷

這篇文章是《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#Benefit_to_Fedora ↩︎
  2. https://btrfs.readthedocs.io/en/latest/Subvolumes.html ↩︎
  3. https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Layout ↩︎
  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中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國