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、隨身碟等。
jffs2和yaffs2檔案系統,都是基於裸的flash來使用,它們並不適用於FTL裝置,FTL裝置使用比較多的檔案系統是:FAT32,exFAT、NTFS、ext3、ext4等
關於儲存介質和其它檔案系統原理的介紹,可以檢視前面文章:
結尾
yaffs2目前在嵌入式裝置中使用率還是比較高,瞭解它的工作原理,有助於更好地使用它。另外從官方資料上看,yaffs 是需要授權收費的,如果有使用yaffs2檔案系統的裝置,需要考慮是否存在版權法律風險。
【如果你覺得文章內容對你有幫助,那就點個贊、關注一下吧】