檔案系統(十一):Linux Squashfs只讀檔案系統介紹

liwen01發表於2024-07-30

liwen01 2024.07.21

前言

嵌入式Linux系統中,squashfs檔案系統使用非常廣泛。它主要的特性是隻讀,檔案壓縮比例高。對於flash空間緊張的系統,可以將一些不需要修改的資源打包成壓縮的只讀檔案系統格式,從而達到節省空間的目的。

另外還有個特性就是它可以分塊解壓縮,使用資料會更加靈活,但同時也會引入讀放大的問題。

(一)製作squash檔案系統

使用mksquashfs可以將檔案及資料夾製作成squash檔案系統映象檔案,比如我們要將squashfs-root資料夾打包成squashfs映象檔案,可以使用命令:

mksquashfs squashfs-root squashfs-root.sqsh -comp xz

這裡是使用xz壓縮方式進行檔案壓縮

(1)壓縮比例測試

squashfs是一個只讀壓縮的檔案系統,我們簡單測試一下它的壓縮功能

使用/dev/zero生成零資料寫入到資料夾squashfs_zero對應的檔案中

dd if=/dev/zero of=file1 bs=256K count=1

製作如下測試檔案目錄及測試檔案:

biao@ubuntu:~/test/squashfs/squashfs_zero$ tree
.
├── test1
│   ├── file1
│   ├── file1_1
│   └── file1_2
├── test2
│   ├── file2
│   ├── file2_1
│   └── file2_2
├── test3
│   ├── file3
│   ├── file3_1
│   └── file3_2
└── test4
    ├── file4
    ├── file4_1
    └── file4_2

4 directories, 12 files
biao@ubuntu:~/test/squashfs/squashfs_zero$

檔案大小如下:

biao@ubuntu:~/test/squashfs/squashfs_zero$ du -h
1.5M    ./test3
2.1M    ./test2
2.1M    ./test1
1.7M    ./test4
7.3M    .
biao@ubuntu:~/test/squashfs/squashfs_zero$

使用xz壓縮方式將squashfs_zero製作成映象檔案

mksquashfs squashfs_zero squashfs_zero.sqsh -comp xz

檔案大小如下:

biao@ubuntu:~/test/squashfs$ ll -h squashfs_zero.sqsh 
-rw-r--r-- 1 biao biao 4.0K Jun 26 23:48 squashfs_zero.sqsh
biao@ubuntu:~/test/squashfs$

這裡是將7.3M大小squashfs_zero資料夾壓縮成了一個4k大小的squashfs_zero.sqsh。當然,這裡的測試是非常極端的,因為檔案寫入的資料都是0,如果寫入隨機數那壓縮比例就會相差非常大了。

(二)squashfs資料分析

(1)資料佈局

Squashfs的一個映象檔案它最多包含下面9個部分:Superblock、Compression options、Data blocks fragments、Inode table、Directory table、Fragment table、Export table、 UID/GID lookup table、Xattr table

最多包含的意思,也就是有些部分不是必須的,比如Compression options 部分。

它們在映象檔案中的資料分佈如下圖:

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

(2)製作測試映象檔案

使用/dev/urandom 生成隨機數寫到資料夾squashfs_urandom對應的檔案:

dd if=/dev/urandom of=filex bs=10K count=50

製作如下測試檔案目錄及測試檔案:

biao@ubuntu:~/test/squashfs/squashfs_urandom$ tree
.
├── test1
│   ├── file1
│   ├── file1_1
│   └── file1_2
├── test2
│   ├── file2
│   ├── file2_1
│   └── file2_2
├── test3
│   ├── file3
│   ├── file3_1
│   └── file3_2
└── test4
    ├── file4
    ├── file4_1
    └── file4_2

4 directories, 12 files
biao@ubuntu:~/test/squashfs/squashfs_urandom$

squashfs 檔案系統的組成部分,大部分也都是壓縮的,為了我們後面的資料分析,我們設定Data blocks fragments、Inode table、Directory table、Fragment table不進行壓縮

製作命令如下:

mksquashfs squashfs_urandom squashfs_urandom.sqsh -comp xz  -noF -noX -noI -noD

(3)檢視映象資料資訊

如果要檢視squashfs的概要資訊,可以使用unsquashfs命令進行檢視

unsquashfs -s squashfs_urandom.sqsh

輸出內容資訊如下:

biao@ubuntu:~/test/squashfs$ unsquashfs -s squashfs_urandom.sqsh 
Found a valid SQUASHFS 4:0 superblock on squashfs_urandom.sqsh.
Creation or last append time Wed Jun 26 23:28:18 2024
Filesystem size 5032.60 Kbytes (4.91 Mbytes)
Compression xz
Block size 131072
Filesystem is exportable via NFS
Inodes are uncompressed
Data is uncompressed
Fragments are uncompressed
Always-use-fragments option is not specified
Xattrs are uncompressed
Duplicates are removed
Number of fragments 2
Number of inodes 37
Number of ids 1
biao@ubuntu:~/test/squashfs$

這裡我們可以看到,上面我們設定-no的部分,是沒有進行資料壓縮的。

(4)Superblock引數分析

Superblock 在映象檔案的最開始位置,大小固定為96個位元組,檢視資料內容如下:

biao@ubuntu:~/test/squashfs$ hexdump  -s 0 -n 96 -C squashfs_urandom.sqsh 
00000000  68 73 71 73 11 00 00 00  ec 5c 7a 66 00 00 02 00  |hsqs.....\zf....|
00000010  02 00 00 00 04 00 11 00  cb 01 01 00 04 00 00 00  |................|
00000020  ac 02 00 00 00 00 00 00  16 9d 4e 00 00 00 00 00  |..........N.....|
00000030  0e 9d 4e 00 00 00 00 00  ff ff ff ff ff ff ff ff  |..N.............|
00000040  60 98 4e 00 00 00 00 00  2e 9b 4e 00 00 00 00 00  |`.N.......N.....|
00000050  6e 9c 4e 00 00 00 00 00  00 9d 4e 00 00 00 00 00  |n.N.......N.....|
00000060
biao@ubuntu:~/test/squashfs$

對Superblock的資料進行解析

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

這裡我們看到幾個比較關鍵的資料

  1. 最開始的4個位元組為squashfs的magic,值為hsqs
  2. block size 是每個資料塊的最大長度,這裡是128KB,squashfs支援的塊大小範圍是:4KB~1MB
  3. compressor 表示壓縮型別,這裡的4表示xz壓縮,其它還支援GZIP、LZMA、LZO、LZ4、ZSTD 資料壓縮格式。
  4. frag count 表示有多少段資料是儲存在fragments組塊中
  5. 最後面是各個table組塊的開始位置

(5)inode table資料分析

從superblock中我們知道inode table的開始位置是在0x4e9860位置

biao@ubuntu:~/test/squashfs$ hexdump  -s 0x4e9860 -n 718 -C squashfs_urandom.sqsh     
004e9860  cc 82 02 00 b4 01 00 00  00 00 9b e9 78 66 02 00  |............xf..|
004e9870  00 00 60 00 00 00 ff ff  ff ff 00 00 00 00 00 20  |..`............ |
004e9880  03 00 00 00 02 01 00 20  01 01 02 00 b4 01 00 00  |....... ........|
004e9890  00 00 c3 e9 78 66 03 00  00 00 60 20 03 00 ff ff  |....xf....` ....|
004e98a0  ff ff 00 00 00 00 00 d0  07 00 00 00 02 01 00 00  |................|
004e98b0  02 01 00 00 02 01 00 d0  01 01 02 00 b4 01 00 00  |................|
004e98c0  00 00 cf e9 78 66 04 00  00 00 60 f0 0a 00 ff ff  |....xf....`.....|
004e98d0  ff ff 00 00 00 00 00 80  0c 00 00 00 02 01 00 00  |................|
004e98e0  02 01 00 00 02 01 00 00  02 01 00 00 02 01 00 00  |................|
004e98f0  02 01 00 80 00 01 01 00  fd 01 00 00 00 00 b1 e9  |................|
004e9900  78 66 01 00 00 00 00 00  00 00 02 00 00 00 3a 00  |xf............:.|
004e9910  00 00 11 00 00 00 02 00  b4 01 00 00 00 00 f9 e9  |................|
004e9920  78 66 06 00 00 00 00 00  00 00 00 00 00 00 00 00  |xf..............|
004e9930  00 00 00 78 00 00 02 00  b4 01 00 00 00 00 01 ea  |...x............|
004e9940  78 66 07 00 00 00 00 00  00 00 00 00 00 00 00 78  |xf.............x|
004e9950  00 00 00 18 01 00 02 00  b4 01 00 00 00 00 08 ea  |................|
004e9960  78 66 08 00 00 00 00 00  00 00 01 00 00 00 00 00  |xf..............|
004e9970  00 00 00 f0 00 00 01 00  fd 01 00 00 00 00 ea e9  |................|
.........
.........
biao@ubuntu:~/test/squashfs$

對資料進行分析

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

這裡有幾個引數需要注意:

(a)inode_type

inode_type 是inode的型別,數值2表示普通檔案,其它型別定義如下:

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

(b)block_sizes

這裡是描述的塊的大小(有可能是壓縮的),這個大小需要解析。

為什麼有些inode有多個block_sizes呢?這個是因為superblock中定義了一個block的最大值,如果一個檔案的大小大於block最大值,那它就存在多個block_sizes。

實際每一個檔案都有一個對應的inode,它都是按序分佈在inode table中。

(6)directory table 資料分析

從superblock中我們知道directory table的開始位置是在0x4e9b2e位置:

biao@ubuntu:~/test/squashfs$ hexdump  -s 0x4e9b2e -n 320 -C squashfs_urandom.sqsh          
004e9b2e  1c 81 02 00 00 00 00 00  00 00 02 00 00 00 00 00  |................|
004e9b3e  00 00 02 00 04 00 66 69  6c 65 31 28 00 01 00 02  |......file1(....|
004e9b4e  00 06 00 66 69 6c 65 31  5f 31 58 00 02 00 02 00  |...file1_1X.....|
004e9b5e  06 00 66 69 6c 65 31 5f  32 02 00 00 00 00 00 00  |..file1_2.......|
004e9b6e  00 06 00 00 00 b4 00 00  00 02 00 04 00 66 69 6c  |.............fil|
004e9b7e  65 32 d4 00 01 00 02 00  06 00 66 69 6c 65 32 5f  |e2........file2_|
004e9b8e  31 f4 00 02 00 02 00 06  00 66 69 6c 65 32 5f 32  |1........file2_2|
004e9b9e  02 00 00 00 00 00 00 00  0a 00 00 00 34 01 00 00  |............4...|
004e9bae  02 00 04 00 66 69 6c 65  33 68 01 01 00 02 00 06  |....file3h......|
004e9bbe  00 66 69 6c 65 33 5f 31  98 01 02 00 02 00 06 00  |.file3_1........|
004e9bce  66 69 6c 65 33 5f 32 02  00 00 00 00 00 00 00 0e  |file3_2.........|
004e9bde  00 00 00 f8 01 00 00 02  00 04 00 66 69 6c 65 34  |...........file4|
004e9bee  28 02 01 00 02 00 06 00  66 69 6c 65 34 5f 31 60  |(.......file4_1`|
004e9bfe  02 02 00 02 00 06 00 66  69 6c 65 34 5f 32 03 00  |.......file4_2..|
004e9c0e  00 00 00 00 00 00 01 00  00 00 94 00 00 00 01 00  |................|
004e9c1e  04 00 74 65 73 74 31 14  01 04 00 01 00 04 00 74  |..test1........t|
004e9c2e  65 73 74 32 d8 01 08 00  01 00 04 00 74 65 73 74  |est2........test|
004e9c3e  33 8c 02 0c 00 01 00 04  00 74 65 73 74 34 20 80  |3........test4 .|
004e9c4e  60 70 17 00 00 00 00 00  00 90 01 01 00 00 00 00  |`p..............|
004e9c5e  60 a8 4d 00 00 00 00 00  00 f0 00 01 00 00 00 00  |`.M.............|
004e9c6e
biao@ubuntu:~/test/squashfs$

對資料進行解析:

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

這裡最開始是一個directory header結構,它由count、start、inode number組成,它們定義如下:

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

每個directory header 至少需要攜帶一個Directory Entry,Directory Entry的定義如下:

檔案系統(十一):Linux Squashfs只讀檔案系統介紹

這裡的inode number 與 inode table 中的inode number是相互對應的

(7)Data blocks fragments 分析

(a)Data blocks

在我們測試的這個映象檔案中,應為使用的是xz壓縮方式,屬於常規壓縮方式,Compression options中不會有描述,也就是說Compression options組成部分是為空。

在Superblock後面緊接著的就是Data blocks資料。

從inode table和dir table我們知道,最開始儲存的是inode number為2的file1 檔案。

因為我們這裡的資料未進行壓縮,正常應該是對比映象檔案0x60地址開始的資料與file1檔案開始的資料一樣的。

(b)fragments

fragments 組塊設計的目的是用來儲存一些小檔案,將它們組合成一個block來儲存,還有一種就是前面檔案剩餘的一小部分資料,也有可能會被儲存在fragments組塊中。

具體哪些資料儲存到了fragments,可以檢視fragments table表

(三)squashfs工作原理

(1)掛載檔案系統

squashfs被掛載的時候,系統首先讀取superblock塊,獲取squashfs基本資訊和各個表格的位置。

(2)訪問檔案或目錄

  • 系統從 superblock 獲取 inode table 和 directory table 的位置。
  • 如果是訪問目錄,系統查詢 directory table,獲取目錄中每個檔案和子目錄的名稱及其 inode 編號。
  • 透過 inode 編號,從 inode table 獲取檔案或目錄的 inode,瞭解檔案的後設資料和資料塊位置。
  • 對於小檔案或大檔案的片段,透過 inode 中的資訊查詢 fragment table,獲取片段的資料位置。

(四)squashfs優缺點

(1)優點

高壓縮率:SquashFS 使用 gzip、lzma、lz4、xz 等壓縮演算法,能夠顯著減少檔案系統的大小,節省儲存空間。

只讀特性:適合用於需要保護資料完整性的環境,如嵌入式系統和作業系統的只讀映象。

高效的隨機訪問:SquashFS 支援高效的隨機讀取訪問,適合讀取頻繁的場景。

碎片處理: 透過 Fragment Table,SquashFS 能有效處理小檔案,減少儲存碎片,提高儲存效率。

儲存和效能最佳化: 支援檔案、目錄和 inode 的壓縮,減少了儲存佔用和 I/O 操作,提高了效能。

資料完整性:SquashFS 可以包含校驗和,用於確保資料的完整性和防止資料損壞。

(2)缺點

只讀特性:SquashFS 是隻讀的,不能直接修改檔案系統中的檔案或目錄。這意味著需要更新或更改檔案系統時,必須重新生成整個檔案系統映象。

壓縮開銷:雖然讀取速度較快,但解壓縮過程仍然需要一定的 CPU 資源。在低效能的嵌入式系統中,這可能會對系統效能產生一定影響。

記憶體消耗:在讀取大檔案時,解壓縮過程可能會消耗大量記憶體,尤其是在資源受限的嵌入式系統中,這可能會成為一個瓶頸

結尾

上面介紹了squashfs檔案系統的資料組成和它們相互工作的原理以及squash檔案系統的優缺點。

這裡提一個問題:如果根檔案系統使用squashfs檔案系統,main執行檔案也位於根檔案系統中,在不考慮雙分割槽備份升級的情況下,要怎麼升級根檔案系統?

在main程式中直接將新squashfs映象檔案寫入到根檔案系統所在的mtdblock中是否可以?會不會存在根檔案系統更新異常的風險?

-------------------End-------------------
如需獲取更多內容
請關注 liwen01 公眾號

相關文章