檔案系統(九):一文看懂yaffs2檔案系統原理

liwen01發表於2024-07-11

liwen01 2024.07.07

前言

yaffs 是專為nand flash 設計的一款檔案系統,與jffs 類似,都是屬於日誌結構檔案系統。與jffs 不同的是,yaffs 檔案系統利用了nand flash 一些特有屬性,所以在資料讀寫擦除和回收上都有較大的差異。

關於jffs2檔案系統的介紹可以檢視檔案系統(八):Linux JFFS2檔案系統工作原理、優勢與侷限

這裡先介紹一下nand flash的一些基礎知識,有助於後面理解yaffs的設計原理。

(一)flash 基礎

flash分為nor flashnand 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

檔案系統(九):一文看懂yaffs2檔案系統原理

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.

檔案系統(九):一文看懂yaffs2檔案系統原理

不同型號不同廠家生產的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 資料打包

  1. 建立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$
  1. 下載最新yaffs原始碼,在yaffs2/utils 目錄執行make,編譯生成mkyaffs2image打包程式
  2. 使用預設引數對測試目錄進行打包
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的資料結構,詳細解析資料如下:

檔案系統(九):一文看懂yaffs2檔案系統原理

從目錄解析表格中我們可以看到如下資訊:
  1. 未填寫區域是資料0xFF,也就是未寫入資料
  2. object_header大小為512Byte
  3. oob 大小為64Byte,與上面程式碼設定的相同
  4. 這裡file_size_low為0xFF,表示不攜帶實際資料,實際也是沒有data段
  5. 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

檔案系統(九):一文看懂yaffs2檔案系統原理

與目錄相比,檔案有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中是需要怎麼操作的呢?

  1. 首先找到要修改的chunk,將資料讀取到記憶體中,再對其資料進行修改,最後將修改後的資料寫入到一個新的chunk
  2. 新的資料寫入新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 idseq_number

(3)垃圾回收機制

從上面資料更新原理上我們知道,一箇舊的資料,或是資料結構,在yaffs2檔案系統中並不會標記它為無效,因為寫入標誌同樣需要擦除再寫入。在yaffs2檔案系統中,是透過seq_number來標記資料版本的新舊,舊的則為無效資料。

在yaffs2的垃圾回收中,有兩種方式:主動回收和被動回收:

主動回收:一個block中的絕大部分chunk資料都是無效的,檔案系統會觸發主動回收

被動回收:flash 已經沒有乾淨的chunk可以繼續使用,此時需要立即執行垃圾回收以釋放空間。這裡會把幾個block中的有效資料合併到一塊,騰出至少一個無效資料block以便進行整塊擦除回收。

yaffs2檔案系統中,為了平衡效能與回收功能,它的垃圾回收有兩個特性:

  1. 儘可能地延遲進行垃圾回收
  2. 一次只處理一個塊

(四)優缺點

(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等

關於儲存介質和其它檔案系統原理的介紹,可以檢視前面文章:

檔案系統(一):儲存介質、原理與架構
檔案系統(二):分割槽、格式化資料結構
檔案系統(三):嵌入式、計算機系統啟動流程與步驟
檔案系統(四):FAT32檔案系統實現原理
檔案系統(五):exFAT 檔案系統原理詳解
檔案系統(六):一文看懂linux ext4檔案系統工作原理
檔案系統(七):檔案系統崩潰一致性、方法、原理與侷限
檔案系統(八):Linux JFFS2檔案系統工作原理、優勢與侷限

結尾

yaffs2目前在嵌入式裝置中使用率還是比較高,瞭解它的工作原理,有助於更好地使用它。另外從官方資料上看,yaffs 是需要授權收費的,如果有使用yaffs2檔案系統的裝置,需要考慮是否存在版權法律風險。

【如果你覺得文章內容對你有幫助,那就點個贊、關注一下吧】

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

相關文章