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 部分。
它們在映象檔案中的資料分佈如下圖:
(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的資料進行解析
這裡我們看到幾個比較關鍵的資料
-
最開始的4個位元組為squashfs的magic,值為hsqs -
block size 是每個資料塊的最大長度,這裡是128KB,squashfs支援的塊大小範圍是:4KB~1MB -
compressor 表示壓縮型別,這裡的4表示xz壓縮,其它還支援GZIP、LZMA、LZO、LZ4、ZSTD 資料壓縮格式。 -
frag count 表示有多少段資料是儲存在fragments組塊中 -
最後面是各個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$
對資料進行分析
這裡有幾個引數需要注意:
(a)inode_type
inode_type 是inode的型別,數值2表示普通檔案,其它型別定義如下:
(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$
對資料進行解析:
這裡最開始是一個directory header結構,它由count、start、inode number組成,它們定義如下:
每個directory header 至少需要攜帶一個Directory Entry,Directory Entry的定義如下:
這裡的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中是否可以?會不會存在根檔案系統更新異常的風險?