liwen01 2024.07.21
前言
UBI (Unsorted Block Images)檔案系統是一種用於裸 flash 的檔案系統管理層。它是專為管理原始快閃記憶體裝置而設計,特別適用於嵌入式系統。與 YAFFS2 和 JFFS2 不同的是,它可以提供整個 flash 空間的磨損平衡,並且有良好的擴充套件性,適用於大容量的 nand flash 。
(一)MTD、UBI 與 UBIFS
前面介紹的 JFFS2 和 YAFFS2 都是執行在 MTD 之上,而 UBIFS 只能執行在 UBI 之上,UBI 又只能執行於 MTD 之上,所以這裡就涉及到 3 個子系統:MTD、UBI、UBIFS。
- MTD 提供了對底層快閃記憶體硬體的抽象和基本管理。
- UBI 在 MTD 之上增加了一層管理,處理快閃記憶體的複雜性並提供邏輯卷管理。
- UBIFS 是在 UBI 捲上執行的檔案系統,充分利用 UBI 的特性,提供高效可靠的檔案儲存。
這裡需要特別注意,這裡所說的快閃記憶體,是指裸 flash,而不是經過FTL轉換後的 U 盤、SD、TF、SSD 等裝置。
在 Linux 中,經過 FTL 轉換後的 U 盤、SD、TF、SSD 等裝置,它們屬於塊裝置,是模擬傳統磁碟設計的一種資料結構,以扇區 sector 為讀寫單位。
而 MTD,它既不是字元裝置,也不是塊裝置,它只是 MTD 裝置
(1) MTD (Memory Technology Device)
MTD 是 Linux 核心中的一個子系統,用於支援不同型別的快閃記憶體裝置,如 NOR Flash 和 NAND Flash。MTD 提供了一個抽象層,使得檔案系統和使用者空間程式可以方便地訪問底層的快閃記憶體硬體。
- MTD 裝置:在 Linux 系統中,MTD 裝置通常以 /dev/mtdX 和 /dev/mtdblockX 的形式出現,其中 X 是裝置編號。
- MTD 子裝置:一個 MTD 裝置可以被劃分為多個子裝置,每個子裝置可以獨立使用。
(2)UBI (Unsorted Block Images)
UBI 是一個在 MTD 裝置之上的管理層,專門為 NAND Flash 設計。UBI 處理了 NAND Flash 固有的一些複雜性,如壞塊管理和磨損均衡(wear leveling)。UBI將快閃記憶體劃分為邏輯擦除塊,並對它們進行管理。
- 壞塊管理:UBI 能夠檢測和管理壞塊,確保資料寫入時不會使用壞塊。
- 磨損均衡:UBI 透過均勻分佈擦寫操作,延長快閃記憶體的使用壽命。
- 邏輯卷:UBI 支援在 MTD 裝置上建立多個邏輯卷,每個卷可以獨立使用。
(3)UBIFS (UBI File System)
UBIFS 是專門為 UBI 設計的檔案系統,直接在 UBI 捲上執行。UBIFS 充分利用 UBI 的功能,提供了高效和可靠的檔案儲存解決方案。
- 動態特性:UBIFS 支援動態調整檔案系統大小,根據需要分配和回收空間。
- 日誌結構:UBIFS 使用日誌結構檔案系統,減少資料損壞的風險並提高寫入效能。
- 壓縮:UBIFS 支援多種壓縮演算法,節省儲存空間。
UBIFS 並不是唯一可以在UBI上執行的檔案系統,理論上絕大部分檔案系統都可以在 UBI 上執行。除了 UBIFS,其它檔案系統在 UBI 上使用效率都不高。
(二)映象檔案製作
(1)UBIFS 映象檔案製作
(a)準備測試檔案
新建4個測試目錄,在目錄中建立測試問價,檔案使用 /dev/urandom 寫入隨機數
biao@ubuntu:~/test/ubifs/ubifs_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/ubifs/ubifs_urandom$
檔案大小資訊如下:
biao@ubuntu:~/test/ubifs$ du ubifs_urandom
1904 ubifs_urandom/test3
168 ubifs_urandom/test2
1504 ubifs_urandom/test1
1476 ubifs_urandom/test4
5056 ubifs_urandom
biao@ubuntu:~/test/ubifs$
(b)製作 UBIFS 映象檔案
mkfs.ubifs -r ubifs_urandom -m 2048 -e 129024 -c 10000 -o ubifs_urandom.ubifs
各引數的作用:
-r, -d, --root=DIR build file system from directory DIR
-m, --min-io-size=SIZE minimum I/O unit size
-e, --leb-size=SIZE logical erase block size
-c, --max-leb-cnt=COUNT maximum logical erase block count
-o, --output=FILE output to FILE
上面命令的作用是:將 ubifs_urandom 目錄裡面的檔案打包成一個頁大小為 2048(2KB)、邏輯擦除塊大小為 129024(126KB)、最大邏輯塊為 10000 的 UBIFS 映象檔案 (ubifs_urandom.ubifs)。
這裡有幾點需要注意:
- -m 是頁大小,不是子頁大小
- -e 設定的邏輯擦除塊大小要與UBI裡的相同,不然掛載的時候會報錯誤
(2)UBI 映象檔案製作
在 UBIFS 的基礎上,製作一個UBI映象檔案
(a)製作 UBI 映象配置檔案
建立配置檔案 ubinize.cfg
[ubifs]
mode=ubi
image=ubifs_urandom.ubifs
vol_id=0
vol_size=256MiB
vol_type=dynamic
vol_name=ubifs_urandom
vol_flags=autoresize
- image 為我們上面製作的UBIFS檔案系統映象檔案
- vol_id 指定卷 ID,這個是在有多個卷的時候使用
- vol_size 定義卷的大小
- vol_type 設定為動態卷,卷的大小可以變化
- vol_name 卷的名字
- vol_flags 自動調整大小
(b)製作 UBI 映象檔案
ubinize -o ubi.img -m 2048 -O 512 -p 128KiB ubinize.cfg
UBI 映象檔案和 UBIFS 的映象檔案,都需要根據實際 Flash 的引數進行設定
上面命令的作用是:將一個 ubifs 映象檔案製作成一個頁大小為 2048(2KB)、子頁大小為 (256Byte),物理擦除塊大小為 128KB 的 UBI 映象檔案。
(三) 掛載 UBIFS 檔案系統
為了方便除錯,我們這裡直接使用PC機上的虛擬 MTD 裝置來模擬Flash。Linux 核心中有 3 種 MTD 裝置模擬器可用:
- mtdram:在 RAM 中模擬 NOR 快閃記憶體;
- nandsim:在 RAM 中模擬 NAND 快閃記憶體;
- block2mtd:在塊裝置上模擬 NOR 快閃記憶體。
(1)載入 nandsim 模組
這裡仿一個 1GiB, 2048 bytes page的 nand flash。
sudo modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=0x95
可以透過 /proc/mtd 和 /dev/mtd0 檢視模擬的 nandflsh 資訊
biao@ubuntu:~/test/ubifs$ cat /proc/mtd
dev: size erasesize name
mtd0: 40000000 00020000 "NAND simulator partition 0"
biao@ubuntu:~/test/ubifs$ mtdinfo /dev/mtd0
mtd0
Name: NAND simulator partition 0
Type: nand
Eraseblock size: 131072 bytes, 128.0 KiB
Amount of eraseblocks: 8192 (1073741824 bytes, 1024.0 MiB)
Minimum input/output unit size: 2048 bytes
Sub-page size: 512 bytes
OOB size: 64 bytes
Character device major/minor: 90:0
Bad blocks are allowed: true
Device is writable: true
biao@ubuntu:~/test/ubifs$
上面載入 nandsim 的時候,有定義4個ID值,具體值需要根據晶片手冊的資料來設定。
第一位元組為製造商程式碼、第二位元組為裝置程式碼、第三、四位元組為Flash特定引數
下面是幾個示例:
modprobe nandsim first_id_byte=0x20 second_id_byte=0x33 - 16MiB, 512 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0x35 - 32MiB, 512 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0x36 - 64MiB, 512 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0x78 - 128MiB, 512 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0x71 - 256MiB, 512 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0xa2 third_id_byte=0x00 fourth_id_byte=0x15 - 64MiB, 2048 bytes page;
modprobe nandsim first_id_byte=0xec second_id_byte=0xa1 third_id_byte=0x00 fourth_id_byte=0x15 - 128MiB, 2048 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0xaa third_id_byte=0x00 fourth_id_byte=0x15 - 256MiB, 2048 bytes page;
modprobe nandsim first_id_byte=0x20 second_id_byte=0xac third_id_byte=0x00 fourth_id_byte=0x15 - 512MiB, 2048 bytes page;
modprobe nandsim first_id_byte=0xec second_id_byte=0xd3 third_id_byte=0x51 fourth_id_byte=0x95 - 1GiB, 2048 bytes page;
(2)掛載 UBIFS 檔案系統
(a) 載入 UBI 核心模組
sudo modprobe ubi mtd=0
這裡將 ubi 載入到了 mtd 的裝置 0 上
(b) 分離 MTD 上的裝置 0
sudo ubidetach /dev/ubi_ctrl -m 0
(c)格式化 MTD 裝置並寫入 UBI 映象檔案
sudo sudo ubiformat /dev/mtd0 -s 512 -f ubi.img
(d)UBI裝置附加回 MTD 裝置 0 上
sudo ubiattach /dev/ubi_ctrl -m 0 -O 512
(e)掛載 UBIFS 到指定目錄
sudo mount -t ubifs ubi0 /home/biao/test/ubifs/ubifs_simulator
(f)檢視掛載狀態
biao@ubuntu:~/test/ubifs$ df -h
Filesystem Size Used Avail Use% Mounted on
......
ubi0 927M 4.8M 923M 1% /home/biao/test/ubifs/ubifs_simulator
......
biao@ubuntu:~/test/ubifs$
biao@ubuntu:~/test/ubifs/ubifs_simulator$ ls
test1 test2 test3 test4
可以看到製作的 UBIFS 映象檔案已經被掛載到了 ubi0 捲上,掛載目錄上的檔案也就是我們的測試檔案。
(四) UBIFS 映象檔案分析
上面我們製作了兩個映象檔案 UBIFS 和 UBI,然後再將 UBI 映象檔案載入到 PC機上的 NAND Flash 模擬器 nandsim 上。要了解 UBIFS 的工作原理,我們有必要對它在 Flash 上的資料結構進行分析。
(1)UBIFS 資料結構
ubifs-media.h 中可以看到 UBIFS 所有資料結構定義,下面這個是通用資料結構,有幻數、crc校驗、序列號、長度、節點型別、節點組型別這些資訊,其中有效節點有11種。
通用頭部結構體定義如下
/**
* struct ubifs_ch - common header node.
* @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
* @crc: CRC-32 checksum of the node header
* @sqnum: sequence number
* @len: full node length
* @node_type: node type
* @group_type: node group type
* @padding: reserved for future, zeroes
*
* Every UBIFS node starts with this common part. If the node has a key, the
* key always goes next.
*/
struct ubifs_ch {
__le32 magic;
__le32 crc;
__le64 sqnum;
__le32 len;
__u8 node_type;
__u8 group_type;
__u8 padding[2];
} __packed;
節點型別定義:
enum {
UBIFS_INO_NODE,
UBIFS_DATA_NODE,
UBIFS_DENT_NODE,
UBIFS_XENT_NODE,
UBIFS_TRUN_NODE,
UBIFS_PAD_NODE,
UBIFS_SB_NODE,
UBIFS_MST_NODE,
UBIFS_REF_NODE,
UBIFS_IDX_NODE,
UBIFS_CS_NODE,
UBIFS_ORPH_NODE,
UBIFS_NODE_TYPES_CNT,
};
(2)UBIFS 節點佈局
我們前面透過 mkfs.ubifs 製作生成的 UBIFS 映象檔案,它包含 5 種節點型別,在映象檔案中的佈局如下圖。
最開始是超級快、後面是兩個Master、 最後面是Index Node,它們各自的功能如下:
Superblock Node: 儲存檔案系統的基本資訊,如大小、狀態、版本等。
Master Node: 儲存檔案系統的當前狀態,包括對日誌頭和根索引節點的指標。
Commit Start Node:標記一個提交操作的開始。它用於在檔案系統崩潰時確定哪些資料是已提交的。
Data Node: 儲存檔案的資料。每個資料節點都與一個檔案的特定部分對應。
Index Node:用於構建UBIFS的索引結構,類似於傳統檔案系統中的索引節點(inode)。
(3)UBIFS 節點工作原理
掛載檔案系統:
- 讀取superblock和master node以恢復檔案系統的基本狀態和重要後設資料指標。
- 初始化其他必要的資料結構和快取。
檔案操作:
- 建立檔案:在索引樹中新增新的索引節點,並分配相應的資料節點來儲存檔案內容。
- 讀寫檔案:透過索引節點找到相應的資料節點,然後執行讀寫操作。
- 修改檔案:修改的資料會寫入新的資料節點,並更新相應的索引節點。
提交:
- 寫入 commit start node 以標記提交的開始。
- 將所有修改的資料節點和索引節點寫入快閃記憶體。
- 更新 master node 以反映最新的檔案系統狀態。
崩潰恢復:
- 檢查 commit start node 以確定哪些提交操作已完成。
- 透過 master node 恢復檔案系統的最新一致狀態。
(4)UBIFS 節點分析
這裡我們只分析superblock節點,其它node也類似
使用 hexdum 檢視 一個 ubifs 映象檔案,最開始的位置就是superblock
00000000 31 18 10 06 e6 e4 54 b6 d9 05 00 00 00 00 00 00 |1.....T.........|
00000010 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 08 00 00 00 00 02 00 35 00 00 00 64 00 00 00 |........5...d...|
00000030 00 00 16 00 00 00 00 00 04 00 00 00 02 00 00 00 |................|
00000040 01 00 00 00 01 00 00 00 08 00 00 00 00 01 00 00 |................|
00000050 04 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 ca 9a 3b 1c fe ed 83 |...........;....|
00000070 7a ef 48 f7 83 2c 10 74 b9 36 09 9b 00 00 00 00 |z.H..,.t.6......|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
.........
對上面資料映象解析,可以看到如下資訊:
(五) UBI 映象檔案分析
(1) UBI 資料佈局
UBI 主要的資料結構是 ubi_ec_hdr 和 ubi_vid_hdr :
ubi_ec_hdr: (Erase Counter Header) 包含擦除計數資訊頭部結構,主要作用是記錄和管理物理擦除塊的擦除次數。
ubi_vid_hdr: (Volume Identifier Header) 包含卷標識資訊頭部結構,主要作用是管理和識別物理卷塊中的資料。
一個UBI卷被分成多個塊,每個塊都有這兩個頭部。ubi_ec_hdr 記錄每個塊被擦除的次數,幫助管理塊的壽命和可靠性。而 ubi_vid_hdr 則確保每個塊在卷中的正確位置和資料完整性。
它們在映象檔案或是 flash 中的資料佈局如下:
(2) 資料分析
檢視 ubi.img 映象檔案的前4KB 資料,這裡需要特別注意的是,UBI的資料是按大端模式儲存的,與之前分析的映象檔案有所不同
biao@ubuntu:~/test/ubifs$ hexdump -s 0 -n 4096 -C ubi.img
00000000 55 42 49 23 01 00 00 00 00 00 00 00 00 00 00 00 |UBI#............|
00000010 00 00 02 00 00 00 08 00 20 d2 d3 a0 00 00 00 00 |........ .......|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 92 3a 9d cd |.............:..|
00000040 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000200 55 42 49 21 01 01 00 05 7f ff ef ff 00 00 00 00 |UBI!............|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000230 00 00 00 00 00 00 00 00 00 00 00 00 b8 25 64 a8 |.............%d.|
00000240 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000800 00 00 08 21 00 00 00 01 00 00 00 00 01 00 00 0d |...!............|
00000810 75 62 69 66 73 5f 75 72 61 6e 64 6f 6d 00 00 00 |ubifs_urandom...|
00000820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000890 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000008a0 00 00 00 00 00 00 00 00 8c 7e c0 aa 00 00 00 00 |.........~......|
000008b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
......
ubi_ec_hdr 的資料解析如下
ubi_vid_hdr 的資料解析如下
(3) UBI工作原理
UBI 的工作原理可以透過下面官方的演示影片來介紹:
壞快管理實現原理可以檢視下面影片:
(六)優缺點
(1)優點
(a)耐用性和可靠性:
磨損均衡:UBI能夠有效地管理快閃記憶體的擦除次數,確保所有的擦除塊均勻地使用,從而延長快閃記憶體的壽命。
壞塊管理:UBI能夠檢測並處理壞塊,確保資料不被寫入這些損壞的區域,提高檔案系統的可靠性。
崩潰恢復:UBI在系統崩潰或掉電後具有良好的恢復能力,能夠儘量減少資料損失。
(b)動態大小管理:
動態分割槽調整:UBI允許動態調整分割槽的大小,這對於儲存需求變化較大的應用非常有用。
靈活的空間管理:UBI可以靈活地管理快閃記憶體空間,支援動態的檔案系統分配和調整。
(c)支援大容量快閃記憶體:UBI支援大容量的快閃記憶體裝置,適合用於需要大量儲存空間的嵌入式系統中。
(2)缺點
(a)複雜性增加:
設計複雜:UBI的實現較為複雜,需要在核心中增加額外的層次來管理快閃記憶體,這增加了系統設計和維護的複雜性。
除錯困難:由於其複雜的機制,UBI的問題排查和除錯比傳統檔案系統更加困難。
(b)資源消耗:
記憶體佔用:UBI需要額外的記憶體來維護其資料結構,對於記憶體資源有限的嵌入式系統可能會帶來一定的壓力。
CPU消耗:UBI的執行需要額外的CPU資源來執行磨損均衡和垃圾回收等任務,可能會影響系統的整體效能。
(c)不適合所有應用:
專用性強:UBI主要針對原始快閃記憶體裝置進行最佳化,對於使用其他儲存介質(如eMMC、SD卡等)的系統,其優勢可能不明顯。
特定領域應用:UBI的設計主要面向嵌入式系統,對於桌面或伺服器系統,其他檔案系統(如EXT4、XFS等)可能更為適合。
結尾
總的來說,UBI檔案系統在需要高可靠性、高耐用性和靈活空間管理的嵌入式系統中表現出色,但其複雜性和資源消耗也需要在具體應用中進行權衡。