liwen01 2024.07.07 前言 yaffs 是專為nand flash 設計的一款文件系統,與jffs 類似,都是屬於日誌結構文件系統。與jffs 不同的是,yaffs 文件系統利用了nand flash 一些特有屬性,所以在數據讀寫擦除和回收上都有較大的差異。 關於jffs2文件系統的 ...
liwen01 2024.07.07
前言
yaffs 是專為nand flash 設計的一款文件系統,與jffs 類似,都是屬於日誌結構文件系統。與jffs 不同的是,yaffs 文件系統利用了nand flash 一些特有屬性,所以在數據讀寫擦除和回收上都有較大的差異。
關於jffs2文件系統的介紹可以查看《文件系統(八):Linux JFFS2文件系統工作原理、優勢與局限》
這裡先介紹一下nand flash的一些基礎知識,有助於後面理解yaffs的設計原理。
(一)flash 基礎
flash分為nor flash和nand flash兩類:
nor flash: 成本較高,容量較小,優點是讀寫數據不容易出錯,比較適用於存儲關鍵數據,比如程式固件、配置參數等。
nand flash :成本較低,相對便宜,容量較大,但是數據比較容易出錯,所以一般都需要有對應的軟體或者硬體的校驗演算法(ECC),比較適合用來儲存大容量且數據安全要求不是非常嚴格的數據,比如照片、視頻等。
(1)nand flash 數據存儲單元
nand flash數據存儲單元從概念上來說,由大到小有:
Nand Flash(Package) -> Chip(Die) -> Plane -> Block -> Page(Chunk) -> OOB(Spare data)
其中有些存儲單元,在一些不同的資料上它們的叫法不太一樣,比如page(頁),、有些資料上介紹的是Chunk,在有些軟體編程中,也有可能被介紹為扇區sector
Nand Flash:也叫Package,這是我們在PCBA上看到的已經封裝好的整科晶元,帶有封裝有IO引腳,可以直接焊接到PCB上使用。
Chip:也叫Die(裸片),這是獨立的矽片,包含存儲單元和控制電路,一個Package 中可以包含多個Die。
Plane : Plane是die內部的一個邏輯分區。每個die通常被劃分為多個plane,以實現並行操作。每個plane有獨立的寄存器和數據緩存,因此可以同時進行多個操作(如讀取、寫入、擦除),從而提高性能。
Block :NAND Flash存儲的基本單位。
Page :也叫chunk,NAND Flash中最小的可編程單元。
OOB(Out-Of-Band) :也叫Spare data,OOB區域是每個page中額外的存儲空間,用於存儲元數據,例如錯誤校正碼(ECC)、壞塊標記和其他管理信息.
(2)nand flash 特性
nand flash 有一些特殊的屬性,也是因為這些特殊的屬性才有了yaffs文件系統的特殊設計
-
數據讀寫的最小單位是page(chunk) -
數據寫入之前,寫入位置需要是被擦除過了的 -
數據擦除的最小單位是block -
block裡面的page,只能按順序寫入,不能任意page寫入 -
oob的數據是隨著page(chunk)的數據一同被寫入 -
nand flash有編程干擾、讀取干擾、配對頁面等問題,會引起自身或是配對頁面的位翻轉。
(3)數據存儲
結合nand flash的特性,從應用軟體編程的角度來看,整個nand flash空間是由各page(chunk)組成,每個page(chunk)後面跟隨一個與之對應的oob.
不同型號不同廠家生產的nand flash,它的block、page、oob等大小有可能不一樣,在軟體開發或是製作yaffs文件系統時,首先需要確認nand flash的參數。
(二)yaff2 數據格式
yaffs 有兩個版本,yaffs1與yaffs2,主要區別是yaffs2可以支持比512Byte更大的chunk。它發佈於2003年,比jffs2晚一兩年被設計,但距今也二十多年了。
下麵內容,yaffs 是代指yaffs1和yaffs2。關於yaffs文件系統的詳細介紹,可以從官方網站下載到最新的代碼和說明文檔:https://www.aleph1.co.uk/gitweb/
(1)yaffs2 數據打包
-
創建4個測試目錄,每個目錄各創建一個測試文件,裡面寫有少量字元數據:
biao@ubuntu:~/test/yaffs/yaffs2_fs$ tree
.
├── test1
│ └── file1
├── test2
│ └── file2
├── test3
│ └── file3
└── test4
└── file4
4 directories, 4 files
在製作成yaffs2鏡像文件之前,4個目錄和文件的大小如下:
biao@ubuntu:~/test/yaffs$ du yaffs2_fs
8 yaffs2_fs/test3
8 yaffs2_fs/test2
8 yaffs2_fs/test1
8 yaffs2_fs/test4
36 yaffs2_fs
biao@ubuntu:~/test/yaffs$
-
下載最新yaffs源碼,在yaffs2/utils 目錄執行make,編譯生成mkyaffs2image打包程式 -
使用預設參數對測試目錄進行打包
biao@ubuntu:~/test/yaffs$ ./mkyaffs2image yaffs2_fs yaffs2_fs.img
mkyaffs2image: image building tool for YAFFS2 built Jul 7 2024
Processing directory yaffs2_fs into image file yaffs2_fs.img
Object 257, yaffs2_fs/test3 is a directory
Object 258, yaffs2_fs/test3/file3 is a file, 1 data chunks written
Object 259, yaffs2_fs/test2 is a directory
Object 260, yaffs2_fs/test2/file2 is a file, 1 data chunks written
Object 261, yaffs2_fs/test1 is a directory
Object 262, yaffs2_fs/test1/file1 is a file, 1 data chunks written
Object 263, yaffs2_fs/test4 is a directory
Object 264, yaffs2_fs/test4/file4 is a file, 1 data chunks written
Operation complete.
16 objects in 5 directories
12 NAND pages
biao@ubuntu:~/test/yaffs$
查看yaffs2_fs.img鏡像文件信息:
biao@ubuntu:~/test/yaffs$ stat yaffs2_fs.img
File: yaffs2_fs.img
Size: 135168 Blocks: 264 IO Block: 4096 regular file
Device: 801h/2049d Inode: 7874075 Links: 1
Access: (0600/-rw-------) Uid: ( 1000/ biao) Gid: ( 1000/ biao)
Access: 2024-07-07 23:12:18.195919283 +0800
Modify: 2024-07-07 23:10:19.798582920 +0800
Change: 2024-07-07 23:10:19.798582920 +0800
Birth: -
biao@ubuntu:~/test/yaffs$
從yaffs2_fs.img鏡像文件中我們看到,打包後的鏡像文件比我們原來的目錄文件要大很多,打包前是36KByte,打包後是132KByte,這是為什麼呢?
(2)yaffs 數據分析
使用hexdunp命令直接查看yaffs2_fs.img鏡像文件數據:
biao@ubuntu:~/test/yaffs$ hexdump -C yaffs2_fs.img
00000000 03 00 00 00 01 00 00 00 ff ff 74 65 73 74 33 00 |..........test3.|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
.........
.........
*
00000840 01 00 00 00 01 01 00 00 ff ff 66 69 6c 65 33 00 |..........file3.|
00000850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000940 00 00 00 00 00 00 00 00 00 00 ff ff b4 81 00 00 |................|
00000950 e8 03 00 00 e8 03 00 00 f4 45 85 66 7e e5 70 66 |.........E.f~.pf|
00000960 43 45 85 66 1d 00 00 00 ff ff ff ff ff ff ff ff |CE.f............|
00000970 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
.........
.........
*
00001080 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 |cccccccccccccccc|
00001090 63 63 63 63 63 63 63 63 63 63 63 63 0a ff ff ff |cccccccccccc....|
000010a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
.........
.........
從hex數據中我們可以直觀的看到文件名信息和文件裡面的數據,也就是說文件名和文件裡面的數據都是未壓縮的。
我們對mkyaffsimage.c的源碼進行分析,在預設參數下mkyaffsimage打包的鏡像文件,它的chunk、spare、block大小信息如下:
#define chunkSize 2048
#define spareSize 64
#define pagesPerBlock 64
yaffs2的鏡像文件是由object_header、data、yaffs_spare 三個部分組成,每個object_header、data 至少占用一個chunk,yaffs_spare 實際上也就是oob數據,是存儲在spare空間。
(3)yaffs2 目錄
我們對上面yaffs2_fs.img的鏡像文件進行分析,先看最開始的數據,是test3目錄obj
00000000 03 00 00 00 01 00 00 00 ff ff 74 65 73 74 33 00 |..........test3.|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000100 00 00 00 00 00 00 00 00 00 00 ff ff fd 41 00 00 |.............A..|
00000110 e8 03 00 00 e8 03 00 00 f4 45 85 66 7e e5 70 66 |.........E.f~.pf|
00000120 43 45 85 66 ff ff ff ff ff ff ff ff ff ff ff ff |CE.f............|
00000130 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
000001c0 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 |................|
000001d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000800 00 10 00 00 01 01 00 00 00 00 00 00 ff ff 00 00 |................|
00000810 25 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff |%...............|
00000820 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
0~0x800 地址的數據是object_header數據結構,後面是oob的數據結構,詳細解析數據如下:
-
未填寫區域是數據0xFF,也就是未寫入數據 -
object_header大小為512Byte -
oob 大小為64Byte,與上面代碼設置的相同 -
這裡file_size_low為0xFF,表示不攜帶實際數據,實際也是沒有data段 -
obj_id 是從0x100(256)開始,在整個文件系統中,obj_id是不重覆的,chunk更新的時候,obj_id保持不變
(3)yaffs2 文件
下麵數據是file3的數據結構
00000840 01 00 00 00 01 01 00 00 ff ff 66 69 6c 65 33 00 |..........file3.|
00000850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000940 00 00 00 00 00 00 00 00 00 00 ff ff b4 81 00 00 |................|
00000950 e8 03 00 00 e8 03 00 00 f4 45 85 66 7e e5 70 66 |.........E.f~.pf|
00000960 43 45 85 66 1d 00 00 00 ff ff ff ff ff ff ff ff |CE.f............|
00000970 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000a00 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 |................|
00000a10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000a30 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................|
00000a40 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00001040 00 10 00 00 02 01 00 00 00 00 00 00 ff ff 00 00 |................|
00001050 26 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff |&...............|
00001060 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00001080 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 |cccccccccccccccc|
00001090 63 63 63 63 63 63 63 63 63 63 63 63 0a ff ff ff |cccccccccccc....|
000010a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00001880 00 10 00 00 02 01 00 00 01 00 00 00 1d 00 00 00 |................|
00001890 00 00 00 00 08 00 00 00 08 00 00 00 ff ff ff ff |................|
000018a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
file3是一個文件,其中包括2個chunk:一個是Object,另外一個是data,其中每個chunk後面有一個與之對應的oob
與目錄相比,文件有file_size_low,chunk_id,還有data chunk。我們看file3實際數據:
biao@ubuntu:~/test/yaffs$ stat yaffs2_fs/test3/file3
File: yaffs2_fs/test3/file3
Size: 29 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 7874095 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ biao) Gid: ( 1000/ biao)
Access: 2024-07-07 23:57:37.355671911 +0800
Modify: 2024-07-07 23:40:14.962499985 +0800
Change: 2024-07-07 23:34:11.067767029 +0800
Birth: -
biao@ubuntu:~/test/yaffs$ cat yaffs2_fs/test3/file3
cccccccccccccccccccccccccccc
biao@ubuntu:~/test/yaffs$
對比發現data chunk中存儲的數據,就是file3文件裡面的實際數據。
(三)工作原理
(1)yaffs2 掛載
上面我們分析了目錄和文件obj的數據結構,實際yaffs還支持其它的文件類型:
enum yaffs_obj_type {
YAFFS_OBJECT_TYPE_UNKNOWN,
YAFFS_OBJECT_TYPE_FILE,
YAFFS_OBJECT_TYPE_SYMLINK,
YAFFS_OBJECT_TYPE_DIRECTORY,
YAFFS_OBJECT_TYPE_HARDLINK,
YAFFS_OBJECT_TYPE_SPECIAL
};
從obj 類型結構體中我們可以看到,還支持軟連接、硬連接和特殊文件類型。它們與常規的文件、目錄一樣,都有object_header 結構,關鍵的元數據信息都是存儲在oob中。
實際yaffs文件系統在掛載的時候,並不需要像jffs2一樣掃描整個flash空間。在yaffs文件系統中,只需要先掃描oob裡面的數據就可以構建出文件、目錄與chunk之間的關係,再結合object_header信息就可以構建出整個文件系統的信息。所以yaffs2在同等大小的文件系統中,掛載速度是會比jffs2快的。
(2)yaffs2數據更新
回顧我們前面介紹的nand flash特性:
-
數據讀寫的最小單位是page(chunk) -
數據寫入之前,寫入位置需要是被擦除過了的 -
數據擦除的最小單位是block -
block裡面的page,只能按順序寫入,不能任意page寫入 -
oob的數據是隨著page的數據一同被寫入
對於我們上面介紹的file3文件,如果我們要對它進行修改或是刪除,在flash中是需要怎麼操作的呢?
-
首先找到要修改的chunk,將數據讀取到記憶體中,再對其數據進行修改,最後將修改後的數據寫入到一個新的chunk -
新的數據寫入新chunk的同時,與它對應的oob數據也會被一同寫入新chunk對應的oob區域
oob的數據是隨著chunk的數據寫入flash中的,但是nand flash 的擦除又是按block進行擦除,如果不擦除,數據又不能重新被寫入,那要怎麼標記file3 存儲原來數據的chunk為無效呢?
在yaffs2中,它是通過oob中的obj id來標記是否同一個數據chunk,通過seq_number來標記哪個chunk的數據是最新的,如果不是最新的,那就是無效的了。
比如在文件系統中,有多個chunk它們有相同的obj id,說明這些chunk都是這個obj id 的不同修改版本的同一組數據,seq_number值最大的是最新的數據,其它的則都是無效數據。每一次修改,seq_number就會增加1。
這裡是通過軟體方法來標記數據無效,實際物理數據是沒有做無效標記的,數據也沒有被清除。物理上的標記無效和數據擦除,是需要等到垃圾回收的時候再對整個block進行擦除操作,這個時候標記的其實不是數據無效,而是chunk未使用.
在數據更新的操作中,核心的參數是obj id 和seq_number。
(3)垃圾回收機制
從上面數據更新原理上我們知道,一個舊的數據,或是數據結構,在yaffs2文件系統中並不會標記它為無效,因為寫入標誌同樣需要擦除再寫入。在yaffs2文件系統中,是通過seq_number來標記數據版本的新舊,舊的則為無效數據。
在yaffs2的垃圾回收中,有兩種方式:主動回收和被動回收:
主動回收:一個block中的絕大部分chunk數據都是無效的,文件系統會觸發主動回收
被動回收:flash 已經沒有乾凈的chunk可以繼續使用,此時需要立即執行垃圾回收以釋放空間。這裡會把幾個block中的有效數據合併到一塊,騰出至少一個無效數據block以便進行整塊擦除回收。
yaffs2文件系統中,為了平衡性能與回收功能,它的垃圾回收有兩個特性:
-
儘可能地延遲進行垃圾回收 -
一次只處理一個塊
(四)優缺點
(1)優點
-
啟動較快:與jffs2相比,它不需要全盤掃描flash空間,所以掛載所花費的時間相對較短。 -
日誌結構:採用日誌結構的設計,在異常斷電等情況下比較容易保持文件系統的一致性。 -
磨損均衡:block內的chunk是按序寫入,加上日誌結構設備使yaffs自帶磨損平衡。但是在垃圾回收的時候,並沒有提供專門的演算法,所以不是嚴格的磨損平衡,帶有一些隨機性。
(2)缺點
-
無壓縮功能:從上面我們對file3文件的分析可以看到,文件數據和元數據都未進行壓縮,這個在對成本敏感的嵌入式設備中,是個劣勢。 -
元數據開銷大: 每個obj都至少需要一個chunk存儲object_header,元數據的開銷大,浪費存儲空間。 -
擴展性差:不適合大容量的存儲設備,管理大規模數據時性能可能下降。
(3)yaffs2與jffs2
yaffs2 文件系統與 jffs2 文件系統非常相似,都是基於裸flash設計的文件系統,jffs2 更常用於nor flash ,而yaffs2 是專為nand flash 而設計。它們都是日誌結構文件系統,都有磨損平衡功能,但也都是隨機磨損平衡。
它們都適合比較小容量的存儲設備,因為jffs2掛載的時候需要全盤掃描flash查找元數據構建文件目錄結構,所以jffs2在大容量存儲設備中數據存儲比較多時,掛載所需要的時間會比較長,耗用的記憶體也會比較多。
yaff2 是將關鍵元數據存儲在oob中,nand flash的oob區域是固定的。掛載的時候只需要掃描oob區域數據就可以了,所以相比較jffs2,yaffs2的掛載啟動速度會比較快一些。
jffs2的數據和元數據都是壓縮的,並且支持多種壓縮演算法,這些yaffs2都沒有,所以空間利用率yaffs2並沒有jffs2高。
在產品功能沒有明顯優勢的前提下,能把產品價格做低其實也是一個非常大的優勢,所以nand flash的應用也越發的普及。但目前nand flash 使用比較多的是集成到FTL(Flash Translation Layer)設備中,比如TF卡,SD卡、SSD、U盤等。
jffs2和yaffs2文件系統,都是基於裸的flash來使用,它們並不適用於FTL設備,FTL設備使用比較多的文件系統是:FAT32,exFAT、NTFS、ext3、ext4等
關於存儲介質和其它文件系統原理的介紹,可以查看前面文章:
結尾
yaffs2目前在嵌入式設備中使用率還是比較高,瞭解它的工作原理,有助於更好地使用它。另外從官方資料上看,yaffs 是需要授權收費的,如果有使用yaffs2文件系統的設備,需要考慮是否存在版權法律風險。
【如果你覺得文章內容對你有幫助,那就點個贊、關註一下吧】