Squashfs 4.0 檔案系統

Squashfs 是一個用於 Linux 的壓縮只讀檔案系統。

它使用 zlib、lz4、lzo、xz 或 zstd 壓縮來壓縮檔案、inode 和目錄。系統中的 inode 非常小,並且所有塊都被打包以最大限度地減少資料開銷。支援大於 4K 的塊大小,最大可達 1M 位元組(預設塊大小為 128K)。

Squashfs 適用於通用的只讀檔案系統用途,適用於歸檔用途(即在可以使用 .tar.gz 檔案的情況下),以及在受限的塊裝置/記憶體系統(例如,嵌入式系統)中,需要低開銷。

郵件列表(核心程式碼):linux-fsdevel@vger.kernel.org 網站:github.com/plougher/squashfs-tools

1. 檔案系統特性

Squashfs 檔案系統特性與 Cramfs 比較

最大檔案系統大小

2^64

256 MiB

最大檔案大小

~ 2 TiB

16 MiB

最大檔案數

無限制

無限制

最大目錄數

無限制

無限制

每個目錄的最大條目數

無限制

無限制

最大塊大小

1 MiB

4 KiB

元資料壓縮

目錄索引

稀疏檔案支援

尾端打包(片段)

可匯出(NFS 等)

硬連結支援

readdir 中的“.”和“..”

真實的 inode 編號

32 位 uids/gids

檔案建立時間

Xattr 支援

ACL 支援

Squashfs 壓縮資料、inode 和目錄。此外,inode 和目錄資料高度壓縮,並在位元組邊界上打包。每個壓縮 inode 的平均長度為 8 位元組(確切長度隨檔案型別而變化,即常規檔案、目錄、符號連結和塊/字元裝置 inode 具有不同的大小)。

2. 使用 Squashfs

由於 squashfs 是隻讀檔案系統,因此必須使用 mksquashfs 程式來建立填充的 squashfs 檔案系統。這個和其他 squashfs 實用程式很可能由您的 linux 發行版打包(稱為 squashfs-tools)。原始碼可以從 github.com/plougher/squashfs-tools 獲取。使用說明也可以從該站點獲取。

2.1 掛載選項

errors=%s

指定 squashfs 錯誤是否觸發核心 panic

continue

錯誤不會觸發 panic(預設)

panic

遇到錯誤時觸發 panic,類似於其他幾個檔案系統(例如 btrfs、ext4、f2fs、GFS2、jfs、ntfs、ubifs)

這允許儲存核心轉儲,有助於分析和除錯損壞。

threads=%s

選擇解壓縮模式或執行緒數

如果設定了 SQUASHFS_CHOICE_DECOMP_BY_MOUNT

single

使用單執行緒解壓縮(預設)

任何時候只能解壓縮一個塊(資料或元資料)。這會將 CPU 和記憶體使用量限制在最低水平,但由於等待解壓縮程式的可用性,在使用多 CPU 機器時,在並行 I/O 工作負載上也會產生較差的效能。

multi

每個核心最多使用兩個並行解壓縮程式

如果您有並行 I/O 工作負載並且您的系統有足夠的記憶體,則使用此選項可能會提高整體 I/O 效能。它根據需求動態分配解壓縮程式。

percpu

每個核心最多使用一個解壓縮程式

它使用 percpu 變數來確保解壓縮在核心之間進行負載平衡。

1|2|3|...

配置用於解壓縮的執行緒數

上限為 num_online_cpus() * 2。

如果 SQUASHFS_CHOICE_DECOMP_BY_MOUNT 設定,並且 SQUASHFS_DECOMP_MULTI 和 SQUASHFS_MOUNT_DECOMP_THREADS 都已設定

2|3|...

配置用於解壓縮的執行緒數

上限為 num_online_cpus() * 2。

3. Squashfs 檔案系統設計

一個 squashfs 檔案系統最多由九個部分組成,這些部分按位元組對齊打包在一起

 ---------------
|  superblock   |
|---------------|
|  compression  |
|    options    |
|---------------|
|  datablocks   |
|  & fragments  |
|---------------|
|  inode table  |
|---------------|
|   directory   |
|     table     |
|---------------|
|   fragment    |
|    table      |
|---------------|
|    export     |
|    table      |
|---------------|
|    uid/gid    |
|  lookup table |
|---------------|
|     xattr     |
|     table     |
 ---------------

壓縮資料塊在從源目錄讀取檔案時寫入檔案系統,並檢查重複項。一旦寫入所有檔案資料,就會寫入已完成的 inode、目錄、片段、匯出、uid/gid 查詢和 xattr 表。

3.1 壓縮選項

壓縮器可以選擇支援壓縮特定選項(例如字典大小)。如果使用了非預設壓縮選項,則將這些選項儲存在此處。

3.2 Inode

元資料(inode 和目錄)在 8K 位元組的塊中壓縮。每個壓縮塊都以兩個位元組的長度為字首,如果該塊未壓縮,則設定最高位。如果設定了 -noI 選項,或者如果壓縮塊大於未壓縮塊,則該塊將未壓縮。

Inode 被打包到元資料塊中,並且不對齊到塊邊界,因此 inode 重疊壓縮塊。Inode 由一個 48 位數字標識,該數字對包含 inode 的壓縮元資料塊的位置以及 inode 放置在該塊中的位元組偏移量進行編碼 (<塊, 偏移量>)。

為了最大化壓縮,每種檔案型別(常規檔案、目錄、裝置等)都有不同的 inode,inode 內容和長度隨型別而變化。

為了進一步最大化壓縮,定義了兩種型別的常規檔案 inode 和目錄 inode:為頻繁出現的常規檔案和目錄最佳化的 inode,以及必須儲存額外資訊的擴充套件型別。

3.3 目錄

與 inode 一樣,目錄被打包到壓縮的元資料塊中,儲存在目錄表中。使用包含目錄的元塊的起始地址和解壓縮塊的偏移量 (<塊, 偏移量>) 訪問目錄。

目錄以稍微複雜的方式組織,而不僅僅是檔名列表。該組織利用了這樣一個事實:檔案的 inode(在大多數情況下)將位於同一個壓縮元資料塊中,因此可以共享起始塊。因此,目錄以兩級列表組織,目錄標頭包含共享起始塊值,以及一系列目錄條目,每個條目共享共享起始塊。一旦/如果 inode 起始塊發生變化,就會寫入新的目錄標頭。目錄標頭/目錄條目列表會重複多次,直到需要為止。

目錄已排序,並且可以包含目錄索引以加快檔案查詢速度。目錄索引儲存每個元塊一個條目,每個條目儲存索引/檔名對映到每個元資料塊中的第一個目錄標頭。目錄按字母順序排序,在查詢時,索引會線性掃描,查詢按字母順序大於正在查詢的檔名的第一個檔名。此時,已經找到了檔名所在的元資料塊的位置。索引的總體思路是確保只需要解壓縮一個元資料塊即可進行查詢,而不管目錄的長度如何。該方案的優點是不需要額外的記憶體開銷,並且不需要磁碟上的額外儲存空間。

3.4 檔案資料

常規檔案由一系列連續的壓縮塊和/或壓縮片段塊(尾端打包塊)組成。每個資料塊的壓縮大小儲存在檔案 inode 中包含的塊列表中。

為了加快讀取“大”檔案(256 Mbytes 或更大)時對資料塊的訪問速度,程式碼實現了一個索引快取,該快取快取從塊索引到磁碟上的資料塊位置的對映。

索引快取允許 Squashfs 處理大檔案(高達 1.75 TiB),同時在磁碟上保留一個簡單且節省空間的塊列表。快取被分成多個槽,最多快取八個 224 GiB 檔案(128 KiB 塊)。更大的檔案使用多個槽,1.75 TiB 檔案使用所有 8 個槽。索引快取旨在節省記憶體,預設使用 16 KiB。

3.5 片段查詢表

常規檔案可以包含一個片段索引,該索引使用片段查詢表對映到磁碟上的片段位置和壓縮大小。此片段查詢表本身以壓縮形式儲存到元資料塊中。第二個索引表用於定位這些。為了加快訪問速度(並且因為它很小),此第二個索引表在掛載時讀取並快取在記憶體中。

3.6 Uid/gid 查詢表

為了提高空間效率,常規檔案儲存 uid 和 gid 索引,這些索引使用 id 查詢錶轉換為 32 位 uids/gids。此表以壓縮形式儲存到元資料塊中。第二個索引表用於定位這些。為了加快訪問速度(並且因為它很小),此第二個索引表在掛載時讀取並快取在記憶體中。

3.7 匯出表

為了使 Squashfs 檔案系統可匯出(透過 NFS 等),檔案系統可以選擇性地(使用 -no-exports Mksquashfs 選項停用)包含一個 inode 編號到 inode 磁碟位置查詢表。這是必需的,以便 Squashfs 將檔案控制代碼中傳遞的 inode 編號對映到磁碟上的 inode 位置,這在匯出程式碼重新例項化過期的/重新整理的 inode 時是必需的。

此表以壓縮形式儲存到元資料塊中。第二個索引表用於定位這些。為了加快訪問速度(並且因為它很小),此第二個索引表在掛載時讀取並快取在記憶體中。

3.8 Xattr 表

xattr 表包含每個 inode 的擴充套件屬性。每個 inode 的 xattr 儲存在一個列表中,每個列表條目包含一個型別、名稱和值欄位。型別欄位對 xattr 字首(“user.”、“trusted.” 等)進行編碼,並且還對應該如何解釋名稱/值欄位進行編碼。當前,該型別指示值是內聯儲存(在這種情況下,值欄位包含 xattr 值)還是離線儲存(在這種情況下,值欄位儲存對實際值的儲存位置的引用)。這允許離線儲存大值,從而提高掃描和查詢效能,並且還允許對值進行去重,該值儲存一次,所有其他出現都儲存對該值的離線引用。

xattr 列表被打包到壓縮的 8K 元資料塊中。為了減少 inode 中的開銷,而不是將 xattr 列表的磁碟上位置儲存在每個 inode 中,而是儲存一個 32 位 xattr id。此 xattr id 使用第二個 xattr id 查詢表對映到 xattr 列表的位置。

4. TODO 和未解決的問題

4.1 TODO 列表

實現 ACL 支援。

4.2 Squashfs 內部快取

Squashfs 中的塊是壓縮的。為了避免重複解壓縮最近訪問的資料,Squashfs 使用兩個小的元資料和片段快取。

該快取不用於檔案資料塊,這些資料塊以正常方式在頁面快取中解壓縮和快取。該快取用於臨時快取由於元資料(即 inode 或目錄)或片段訪問而讀取的片段和元資料塊。由於元資料和片段被打包到塊中(以獲得更大的壓縮),因此讀取特定的元資料或片段將檢索已與其打包的其他元資料/片段,由於引用區域性性,這些元資料/片段可能在不久的將來被讀取。臨時快取它們可確保它們可用於不久的將來的訪問,而無需額外的讀取和解壓縮。

將來,此內部快取可能會被使用核心頁面快取的實現所取代。由於頁面快取以頁面大小的單元執行,這可能會在鎖定和相關的競爭條件方面引入額外的複雜性。