深入理解 ext4 等 Linux 檔案系統
瞭解 ext4 的歷史,包括其與 ext3 和之前的其它檔案系統之間的區別。
目前的大部分 Linux 檔案系統都預設採用 ext4 檔案系統,正如以前的 Linux 發行版預設使用 ext3、ext2 以及更久前的 ext。
對於不熟悉 Linux 或檔案系統的朋友而言,你可能不清楚 ext4 相對於上一版本 ext3 帶來了什麼變化。你可能還想知道在一連串關於替代的檔案系統例如 Btrfs、XFS 和 ZFS 不斷被髮布的情況下,ext4 是否仍然能得到進一步的發展。
在一篇文章中,我們不可能講述檔案系統的所有方面,但我們嘗試讓你儘快瞭解 Linux 預設檔案系統的發展歷史,包括它的誕生以及未來發展。
我仔細研究了維基百科裡的各種關於 ext 檔案系統文章、kernel.org 的 wiki 中關於 ext4 的條目以及結合自己的經驗寫下這篇文章。
ext 簡史
MINIX 檔案系統
在有 ext 之前,使用的是 MINIX 檔案系統。如果你不熟悉 Linux 歷史,那麼可以理解為 MINIX 是用於 IBM PC/AT 微型計算機的一個非常小的類 Unix 系統。Andrew Tannenbaum 為了教學的目的而開發了它,並於 1987 年釋出了原始碼(以印刷版的格式!)。
雖然你可以細讀 MINIX 的原始碼,但實際上它並不是自由開源軟體(FOSS)。出版 Tannebaum 著作的出版商要求你花 69 美元的許可費來執行 MINIX,而這筆費用包含在書籍的費用中。儘管如此,在那時來說非常便宜,並且 MINIX 的使用得到迅速發展,很快超過了 Tannebaum 當初使用它來教授作業系統編碼的意圖。在整個 20 世紀 90 年代,你可以發現 MINIX 的安裝在世界各個大學裡面非常流行。而此時,年輕的 Linus Torvalds 使用 MINIX 來開發原始 Linux 核心,並於 1991 年首次公佈,而後在 1992 年 12 月在 GPL 開源協議下發布。
但是等等,這是一篇以 檔案系統 為主題的文章不是嗎?是的,MINIX 有自己的檔案系統,早期的 Linux 版本依賴於它。跟 MINIX 一樣,Linux 的檔案系統也如同玩具那般小 —— MINIX 檔案系統最多能處理 14 個字元的檔名,並且只能處理 64MB 的儲存空間。到了 1991 年,一般的硬碟尺寸已經達到了 40-140 MB。很顯然,Linux 需要一個更好的檔案系統。
ext
當 Linus 開發出剛起步的 Linux 核心時,Rémy Card 從事第一代的 ext 檔案系統的開發工作。ext 檔案系統在 1992 年首次實現併發布 —— 僅在 Linux 首次釋出後的一年!—— ext 解決了 MINIX 檔案系統中最糟糕的問題。
1992 年的 ext 使用在 Linux 核心中的新虛擬檔案系統(VFS)抽象層。與之前的 MINIX 檔案系統不同的是,ext 可以處理高達 2 GB 儲存空間並處理 255 個字元的檔名。
但 ext 並沒有長時間佔統治地位,主要是由於它原始的時間戳(每個檔案僅有一個時間戳,而不是今天我們所熟悉的有 inode、最近檔案訪問時間和最新檔案修改時間的時間戳。)僅僅一年後,ext2 就替代了它。
ext2
Rémy 很快就意識到 ext 的侷限性,所以一年後他設計出 ext2 替代它。當 ext 仍然根植於 “玩具” 作業系統時,ext2 從一開始就被設計為一個商業級檔案系統,沿用 BSD 的 Berkeley 檔案系統的設計原理。
ext2 提供了 GB 級別的最大檔案大小和 TB 級別的檔案系統大小,使其在 20 世紀 90 年代的地位牢牢鞏固在檔案系統大聯盟中。很快它被廣泛地使用,無論是在 Linux 核心中還是最終在 MINIX 中,且利用第三方模組可以使其應用於 MacOS 和 Windows。
但這裡仍然有一些問題需要解決:ext2 檔案系統與 20 世紀 90 年代的大多數檔案系統一樣,如果在將資料寫入到磁碟的時候,系統發生崩潰或斷電,則容易發生災難性的資料損壞。隨著時間的推移,由於碎片(單個檔案儲存在多個位置,物理上其分散在旋轉的磁碟上),它們也遭受了嚴重的效能損失。
儘管存在這些問題,但今天 ext2 還是用在某些特殊的情況下 —— 最常見的是,作為行動式 USB 驅動器的檔案系統格式。
ext3
1998 年,在 ext2 被採用後的 6 年後,Stephen Tweedie 宣佈他正在致力於改進 ext2。這成了 ext3,並於 2001 年 11 月在 2.4.15 核心版本中被採用到 Linux 核心主線中。
20 世紀 90 年代中期的 Packard Bell 計算機,Spacekid,CC0
在大部分情況下,ext2 在 Linux 發行版中工作得很好,但像 FAT、FAT32、HFS 和當時的其它檔案系統一樣 —— 在斷電時容易發生災難性的破壞。如果在將資料寫入檔案系統時候發生斷電,則可能會將其留在所謂 不一致 的狀態 —— 事情只完成一半而另一半未完成。這可能導致大量檔案丟失或損壞,這些檔案與正在儲存的檔案無關甚至導致整個檔案系統無法解除安裝。
ext3 和 20 世紀 90 年代後期的其它檔案系統,如微軟的 NTFS,使用 日誌 來解決這個問題。日誌是磁碟上的一種特殊的分配區域,其寫入被儲存在事務中;如果該事務完成磁碟寫入,則日誌中的資料將提交給檔案系統自身。如果系統在該操作提交前崩潰,則重新啟動的系統識別其為未完成的事務而將其進行回滾,就像從未發生過一樣。這意味著正在處理的檔案可能依然會丟失,但檔案系統 本身 保持一致,且其它所有資料都是安全的。
在使用 ext3 檔案系統的 Linux 核心中實現了三個級別的日誌記錄方式:日記journal、順序ordered和回寫writeback。
- 日記 是最低風險模式,在將資料和元資料提交給檔案系統之前將其寫入日誌。這可以保證正在寫入的檔案與整個檔案系統的一致性,但其顯著降低了效能。
- 順序 是大多數 Linux 發行版預設模式;順序模式將元資料寫入日誌而直接將資料提交到檔案系統。顧名思義,這裡的操作順序是固定的:首先,元資料提交到日誌;其次,資料寫入檔案系統,然後才將日誌中關聯的元資料更新到檔案系統。這確保了在發生崩潰時,那些與未完整寫入相關聯的元資料仍在日誌中,且檔案系統可以在回滾日誌時清理那些不完整的寫入事務。在順序模式下,系統崩潰可能導致在崩潰期間檔案的錯誤被主動寫入,但檔案系統它本身 —— 以及未被主動寫入的檔案 —— 確保是安全的。
- 回寫 是第三種模式 —— 也是最不安全的日誌模式。在回寫模式下,像順序模式一樣,元資料會被記錄到日誌,但資料不會。與順序模式不同,元資料和資料都可以以任何有利於獲得最佳效能的順序寫入。這可以顯著提高效能,但安全性低很多。儘管回寫模式仍然保證檔案系統本身的安全性,但在崩潰或崩潰之前寫入的檔案很容易丟失或損壞。
跟之前的 ext2 類似,ext3 使用 16 位內部定址。這意味著對於有著 4K 塊大小的 ext3 在最大規格為 16 TiB 的檔案系統中可以處理的最大檔案大小為 2 TiB。
ext4
Theodore Ts’o(是當時 ext3 主要開發人員)在 2006 年發表的 ext4,於兩年後在 2.6.28 核心版本中被加入到了 Linux 主線。
Ts’o 將 ext4 描述為一個顯著擴充套件 ext3 但仍然依賴於舊技術的臨時技術。他預計 ext4 終將會被真正的下一代檔案系統所取代。
ext4 在功能上與 ext3 在功能上非常相似,但支援大檔案系統,提高了對碎片的抵抗力,有更高的效能以及更好的時間戳。
ext4 vs ext3
ext3 和 ext4 有一些非常明確的差別,在這裡集中討論下。
向後相容性
ext4 特地設計為儘可能地向後相容 ext3。這不僅允許 ext3 檔案系統原地升級到 ext4;也允許 ext4 驅動程式以 ext3 模式自動掛載 ext3 檔案系統,因此使它無需單獨維護兩個程式碼庫。
大檔案系統
ext3 檔案系統使用 32 位定址,這限制它僅支援 2 TiB 檔案大小和 16 TiB 檔案系統系統大小(這是假設在塊大小為 4 KiB 的情況下,一些 ext3 檔案系統使用更小的塊大小,因此對其進一步被限制)。
ext4 使用 48 位的內部定址,理論上可以在檔案系統上分配高達 16 TiB 大小的檔案,其中檔案系統大小最高可達 1000000 TiB(1 EiB)。在早期 ext4 的實現中有些使用者空間的程式仍然將其限制為最大大小為 16 TiB 的檔案系統,但截至 2011 年,e2fsprogs 已經直接支援大於 16 TiB 大小的 ext4 檔案系統。例如,紅帽企業 Linux 在其合同上僅支援最高 50 TiB 的 ext4 檔案系統,並建議 ext4 卷不超過 100 TiB。
分配方式改進
ext4 在將儲存塊寫入磁碟之前對儲存塊的分配方式進行了大量改進,這可以顯著提高讀寫效能。
區段
區段extent是一系列連續的物理塊 (最多達 128 MiB,假設塊大小為 4 KiB),可以一次性保留和定址。使用區段可以減少給定檔案所需的 inode 數量,並顯著減少碎片並提高寫入大檔案時的效能。
多塊分配
ext3 為每一個新分配的塊呼叫一次塊分配器。當多個寫入同時開啟分配器時,很容易導致嚴重的碎片。然而,ext4 使用延遲分配,這允許它合併寫入並更好地決定如何為尚未提交的寫入分配塊。
持久的預分配
在為檔案預分配磁碟空間時,大部分檔案系統必須在建立時將零寫入該檔案的塊中。ext4 允許替代使用 fallocate()
,它保證了空間的可用性(並試圖為它找到連續的空間),而不需要先寫入它。這顯著提高了寫入和將來讀取流和資料庫應用程式的寫入資料的效能。
延遲分配
這是一個耐人尋味而有爭議性的功能。延遲分配允許 ext4 等待分配將寫入資料的實際塊,直到它準備好將資料提交到磁碟。(相比之下,即使資料仍然在往寫入快取中寫入,ext3 也會立即分配塊。)
當快取中的資料累積時,延遲分配塊允許檔案系統對如何分配塊做出更好的選擇,降低碎片(寫入,以及稍後的讀)並顯著提升效能。然而不幸的是,它 增加 了還沒有專門呼叫 fsync()
方法(當程式設計師想確保資料完全重新整理到磁碟時)的程式的資料丟失的可能性。
假設一個程式完全重寫了一個檔案:
1 | fd=open("file",O_TRUNC);write(fd,data);close(fd); |
使用舊的檔案系統,close(fd);
足以保證 file
中的內容重新整理到磁碟。即使嚴格來說,寫不是事務性的,但如果檔案關閉後發生崩潰,則丟失資料的風險很小。
如果寫入不成功(由於程式上的錯誤、磁碟上的錯誤、斷電等),檔案的原始版本和較新版本都可能丟失資料或損壞。如果其它程序在寫入檔案時訪問檔案,則會看到損壞的版本。如果其它程序開啟檔案並且不希望其內容發生更改 —— 例如,對映到多個正在執行的程式的共享庫。這些程序可能會崩潰。
為了避免這些問題,一些程式設計師完全避免使用 O_TRUNC
。相反,他們可能會寫入一個新檔案,關閉它,然後將其重新命名為舊檔名:
1 | fd=open("newfile");write(fd,data);close(fd);rename("newfile","file"); |
在 沒有 延遲分配的檔案系統下,這足以避免上面列出的潛在的損壞和崩潰問題:因為 rename()
是原子操作,所以它不會被崩潰中斷;並且執行的程式將繼續引用舊的檔案。現在 file
的未連結版本只要有一個開啟的檔案檔案控制代碼即可。但是因為 ext4 的延遲分配會導致寫入被延遲和重新排序,rename("newfile", "file")
可以在 newfile
的內容實際寫入磁碟內容之前執行,這出現了並行進行再次獲得 file
壞版本的問題。
為了緩解這種情況,Linux 核心(自版本 2.6.30)嘗試檢測這些常見程式碼情況並強制立即分配。這會減少但不能防止資料丟失的可能性 —— 並且它對新檔案沒有任何幫助。如果你是一位開發人員,請注意:保證資料立即寫入磁碟的唯一方法是正確呼叫 fsync()
。
無限制的子目錄
ext3 僅限於 32000 個子目錄;ext4 允許無限數量的子目錄。從 2.6.23 核心版本開始,ext4 使用 HTree 索引來減少大量子目錄的效能損失。
日誌校驗
ext3 沒有對日誌進行校驗,這給處於核心直接控制之外的磁碟或自帶快取的控制器裝置帶來了問題。如果控制器或具自帶快取的磁碟脫離了寫入順序,則可能會破壞 ext3 的日記事務順序,從而可能破壞在崩潰期間(或之前一段時間)寫入的檔案。
理論上,這個問題可以使用寫入障礙barrier —— 在安裝檔案系統時,你在掛載選項設定 barrier=1
,然後裝置就會忠實地執行 fsync
一直向下到底層硬體。通過實踐,可以發現儲存裝置和控制器經常不遵守寫入障礙 —— 提高效能(和跟競爭對手比較的效能基準),但增加了本應該防止資料損壞的可能性。
對日誌進行校驗和允許檔案系統崩潰後第一次掛載時意識到其某些條目是無效或無序的。因此,這避免了回滾部分條目或無序日誌條目的錯誤,並進一步損壞的檔案系統 —— 即使部分儲存裝置假做或不遵守寫入障礙。
快速檔案系統檢查
在 ext3 下,在 fsck
被呼叫時會檢查整個檔案系統 —— 包括已刪除或空檔案。相比之下,ext4 標記了 inode 表未分配的塊和扇區,從而允許 fsck
完全跳過它們。這大大減少了在大多數檔案系統上執行 fsck
的時間,它實現於核心 2.6.24。
改進的時間戳
ext3 提供粒度為一秒的時間戳。雖然足以滿足大多數用途,但任務關鍵型應用程式經常需要更嚴格的時間控制。ext4 通過提供納秒級的時間戳,使其可用於那些企業、科學以及任務關鍵型的應用程式。
ext3 檔案系統也沒有提供足夠的位來儲存 2038 年 1 月 18 日以後的日期。ext4 在這裡增加了兩個位,將 Unix 紀元擴充套件了 408 年。如果你在公元 2446 年讀到這篇文章,你很有可能已經轉移到一個更好的檔案系統 —— 如果你還在測量自 1970 年 1 月 1 日 00:00(UTC)以來的時間,這會讓我死後得以安眠。
線上碎片整理
ext2 和 ext3 都不直接支援線上碎片整理 —— 即在掛載時會對檔案系統進行碎片整理。ext2 有一個包含的實用程式 e2defrag
,它的名字暗示 —— 它需要在檔案系統未掛載時離線執行。(顯然,這對於根檔案系統來說非常有問題。)在 ext3 中的情況甚至更糟糕 —— 雖然 ext3 比 ext2 更不容易受到嚴重碎片的影響,但 ext3 檔案系統執行 e2defrag
可能會導致災難性損壞和資料丟失。
儘管 ext3 最初被認為“不受碎片影響”,但對同一檔案(例如 BitTorrent)採用大規模並行寫入過程的過程清楚地表明情況並非完全如此。一些使用者空間的手段和解決方法,例如 Shake,以這樣或那樣方式解決了這個問題 —— 但它們比真正的、檔案系統感知的、核心級碎片整理過程更慢並且在各方面都不太令人滿意。
ext4 通過 e4defrag
解決了這個問題,且是一個線上、核心模式、檔案系統感知、塊和區段級別的碎片整理實用程式。
正在進行的 ext4 開發
ext4,正如 Monty Python 中瘟疫感染者曾經說過的那樣,“我還沒死呢!”雖然它的主要開發人員認為它只是一個真正的下一代檔案系統的權宜之計,但是在一段時間內,沒有任何可能的候選人準備好(由於技術或許可問題)部署為根檔案系統。
在未來的 ext4 版本中仍然有一些關鍵功能要開發,包括元資料校驗和、一流的配額支援和大分配塊。
元資料校驗和
由於 ext4 具有冗餘超級塊,因此為檔案系統校驗其中的元資料提供了一種方法,可以自行確定主超級塊是否已損壞並需要使用備用塊。可以在沒有校驗和的情況下,從損壞的超級塊恢復 —— 但是使用者首先需要意識到它已損壞,然後嘗試使用備用方法手動掛載檔案系統。由於在某些情況下,使用損壞的主超級塊安裝檔案系統讀寫可能會造成進一步的損壞,即使是經驗豐富的使用者也無法避免,這也不是一個完美的解決方案!
與 Btrfs 或 ZFS 等下一代檔案系統提供的極其強大的每塊校驗和相比,ext4 的元資料校驗和的功能非常弱。但它總比沒有好。雖然校驗 所有的事情 都聽起來很簡單!—— 事實上,將校驗和與檔案系統連線到一起有一些重大的挑戰;請參閱設計文件瞭解詳細資訊。
一流的配額支援
等等,配額?!從 ext2 出現的那天開始我們就有了這些!是的,但它們一直都是事後的新增的東西,而且它們總是犯傻。這裡可能不值得詳細介紹,但設計文件列出了配額將從使用者空間移動到核心中的方式,並且能夠更加正確和高效地執行。
大分配塊
隨著時間的推移,那些討厭的儲存系統不斷變得越來越大。由於一些固態硬碟已經使用 8K 硬體塊大小,因此 ext4 對 4K 模組的當前限制越來越受到限制。較大的儲存塊可以顯著減少碎片並提高效能,代價是增加“鬆弛”空間(當你只需要塊的一部分來儲存檔案或檔案的最後一塊時留下的空間)。
你可以在設計文件中檢視詳細說明。
ext4 的實際限制
ext4 是一個健壯、穩定的檔案系統。如今大多數人都應該在用它作為根檔案系統,但它無法處理所有需求。讓我們簡單地談談你不應該期待的一些事情 —— 現在或可能在未來:
雖然 ext4 可以處理高達 1 EiB 大小(相當於 1,000,000 TiB)大小的資料,但你 真的 不應該嘗試這樣做。除了能夠記住更多塊的地址之外,還存在規模上的問題。並且現在 ext4 不會處理(並且可能永遠不會)超過 50-100 TiB 的資料。
ext4 也不足以保證資料的完整性。隨著日誌記錄的重大進展又回到了 ext3 的那個時候,它並未涵蓋資料損壞的許多常見原因。如果資料已經在磁碟上被破壞 —— 由於故障硬體,宇宙射線的影響(是的,真的),或者只是資料隨時間衰減 —— ext4 無法檢測或修復這種損壞。
基於上面兩點,ext4 只是一個純 檔案系統,而不是儲存卷管理器。這意味著,即使你有多個磁碟 —— 也就是奇偶校驗或冗餘,理論上你可以從 ext4 中恢復損壞的資料,但無法知道使用它是否對你有利。雖然理論上可以在不同的層中分離檔案系統和儲存卷管理系統而不會丟失自動損壞檢測和修復功能,但這不是當前儲存系統的設計方式,並且它將給新設計帶來重大挑戰。
備用檔案系統
在我們開始之前,提醒一句:要非常小心,沒有任何備用的檔案系統作為主線核心的一部分而內建和直接支援!
即使一個檔案系統是 安全的,如果在核心升級期間出現問題,使用它作為根檔案系統也是非常可怕的。如果你沒有充分的理由通過一個 chroot 去使用替代介質引導,耐心地操作核心模組、grub 配置和 DKMS……不要在一個很重要的系統中去掉預留的根檔案。
可能有充分的理由使用你的發行版不直接支援的檔案系統 —— 但如果你這樣做,我強烈建議你在系統啟動並可用後再安裝它。(例如,你可能有一個 ext4 根檔案系統,但是將大部分資料儲存在 ZFS 或 Btrfs 池中。)
XFS
XFS 與非 ext 檔案系統在 Linux 中的主線中的地位一樣。它是一個 64 位的日誌檔案系統,自 2001 年以來內置於 Linux 核心中,為大型檔案系統和高度併發性提供了高效能(即大量的程序都會立即寫入檔案系統)。
從 RHEL 7 開始,XFS 成為 Red Hat Enterprise Linux 的預設檔案系統。對於家庭或小型企業使用者來說,它仍然有一些缺點 —— 最值得注意的是,重新調整現有 XFS 檔案系統是一件非常痛苦的事情,不如建立另一個並複製資料更有意義。
雖然 XFS 是穩定的且是高效能的,但它和 ext4 之間沒有足夠具體的最終用途差異,以值得推薦在非預設(如 RHEL7)的任何地方使用它,除非它解決了對 ext4 的特定問題,例如大於 50 TiB 容量的檔案系統。
XFS 在任何方面都不是 ZFS、Btrfs 甚至 WAFL(一個專有的 SAN 檔案系統)的“下一代”檔案系統。就像 ext4 一樣,它應該被視為一種更好的方式的權宜之計。
ZFS
ZFS 由 Sun Microsystems 開發,以 zettabyte 命名 —— 相當於 1 萬億 GB —— 因為它理論上可以解決大型儲存系統。
作為真正的下一代檔案系統,ZFS 提供卷管理(能夠在單個檔案系統中處理多個單獨的儲存裝置),塊級加密校驗和(允許以極高的準確率檢測資料損壞),自動損壞修復(其中冗餘或奇偶校驗儲存可用),快速非同步增量複製,內聯壓縮等,以及更多。
從 Linux 使用者的角度來看,ZFS 的最大問題是許可證問題。ZFS 許可證是 CDDL 許可證,這是一種與 GPL 衝突的半許可的許可證。關於在 Linux 核心中使用 ZFS 的意義存在很多爭議,其爭議範圍從“它是 GPL 違規”到“它是 CDDL 違規”到“它完全沒問題,它還沒有在法庭上進行過測試。”最值得注意的是,自 2016 年以來 Canonical 已將 ZFS 程式碼內聯在其預設核心中,而且目前尚無法律挑戰。
此時,即使我作為一個非常狂熱於 ZFS 的使用者,我也不建議將 ZFS 作為 Linux 的根檔案系統。如果你想在 Linux 上利用 ZFS 的優勢,用 ext4 設定一個小的根檔案系統,然後將 ZFS 用在你剩餘的儲存上,把資料、應用程式以及你喜歡的東西放在它上面 —— 但把 root 分割槽保留在 ext4 上,直到你的發行版明確支援 ZFS 根目錄。
Btrfs
Btrfs 是 B-Tree Filesystem 的簡稱,通常發音為 “butter” —— 由 Chris Mason 於 2007 年在 Oracle 任職期間釋出。Btrfs 旨在跟 ZFS 有大部分相同的目標,提供多種裝置管理、每塊校驗、非同步複製、直列壓縮等,還有更多。
截至 2018 年,Btrfs 相當穩定,可用作標準的單磁碟檔案系統,但可能不應該依賴於卷管理器。與許多常見用例中的 ext4、XFS 或 ZFS 相比,它存在嚴重的效能問題,其下一代功能 —— 複製、多磁碟拓撲和快照管理 —— 可能非常多,其結果可能是從災難性地效能降低到實際資料的丟失。
Btrfs 的維持狀態是有爭議的;SUSE Enterprise Linux 在 2015 年採用它作為預設檔案系統,而 Red Hat 於 2017 年宣佈它從 RHEL 7.4 開始不再支援 Btrfs。可能值得注意的是,該產品支援 Btrfs 部署用作單磁碟檔案系統,而不是像 ZFS 中的多磁碟卷管理器,甚至 Synology 在它的儲存裝置使用 Btrfs,但是它在傳統 Linux 核心 RAID(mdraid)之上分層來管理磁碟。