Yaffs(Yet Another Flash File System)文件系統(tǒng)是專門針對NAND閃存設(shè)計(jì)的嵌入式文件系統(tǒng),目前有YAFFS和YAFFS2兩個(gè)版本,兩個(gè)版本的主要區(qū)別之一在于YAFFS2能夠更好的支持大容量的NAND FLASH芯片。
Yaffs文件系統(tǒng)有些類似于JFFS/JFFS2文件系統(tǒng),與之不同的是JFFS1/2文件系統(tǒng)最初是針對NOR FLASH的應(yīng)用場合設(shè)計(jì)的,而NOR FLASH和NAND FLASH本質(zhì)上有較大的區(qū)別,所以盡管JFFS1/2 文件系統(tǒng)也能應(yīng)用于NAND FLASH,但由于它在內(nèi)存占用和啟動(dòng)時(shí)間方面針對NOR的特性做了一些取舍,所以對NAND來說通常并不是最優(yōu)的方案。
基本上NOR比較適合存儲程序代碼,其容量一般較小(比如小于32MB),價(jià)格較高,而NAND容量可達(dá)1GB以上,價(jià)格也相對便宜,適合存儲數(shù)據(jù)。一般來說,128MB以下容量NAND FLASH 芯片的一頁大小為528字節(jié),用來存放數(shù)據(jù),另外每一頁還有16字節(jié)的備用空間(SpareData,OOB),用來存儲ECC校驗(yàn)/壞塊標(biāo)志等信息,再由若干頁組成一個(gè)塊,通常一塊為32頁16K。
與NOR相比,NAND不是完全可靠的,每塊芯片出廠時(shí)都有一定比例的壞塊存在,對數(shù)據(jù)的存取不是使用地址映射而是通過寄存器的操作,串行存取數(shù)據(jù)。
Yaffs對文件系統(tǒng)上的所有內(nèi)容(比如正常文件,目錄,鏈接,設(shè)備文件等等)都統(tǒng)一當(dāng)作文件來處理,每個(gè)文件都有一個(gè)頁面專門存放文件頭,文件頭保存了文件的模式、所有者id、組id、長度、文件名、Parent Object ID等信息。因?yàn)樾枰谝豁搩?nèi)放下這些內(nèi)容,所以對文件名的長度,符號鏈接對象的路徑名等長度都有限制。
前面說到對于NAND FLASH上的每一頁數(shù)據(jù),都有額外的空間用來存儲附加信息,通常NAND驅(qū)動(dòng)只使用了這些空間的一部分,Yaffs正是利用了這部分空間中剩余的部分來存儲文件系統(tǒng)相關(guān)的內(nèi)容。以512+16B為一個(gè)PAGE的NAND FLASH芯片為例,Yaffs文件系統(tǒng)數(shù)據(jù)的存儲布局如下所示:
0..511 | 數(shù)據(jù)區(qū)域 |
512..515 | YAFFS TAG |
516 | Data status byte |
517 | Block status byte 壞塊標(biāo)志位 |
518..519 | YAFFS TAG |
520..522 | 后256字節(jié)數(shù)據(jù)的ECC校驗(yàn)結(jié)果 |
523..524 | YAFFS TAG |
525..527 | 前256字節(jié)數(shù)據(jù)的ECC校驗(yàn)結(jié)果 |
可以看到在這里YAFFS一共使用了8個(gè)BYTE用來存放文件系統(tǒng)相關(guān)的信息(yaffs_Tags)。這8個(gè)Byte的具體使用情況按順序如下:
Bits | Content |
20 | ChunkID,該page在一個(gè)文件內(nèi)的索引號,所以文件大小被限制在2^20 PAGE 即512Mb |
2 | 2 bits serial number |
10 | ByteCount 該page內(nèi)的有效字節(jié)數(shù) |
18 | ObjectID 對象ID號,用來唯一標(biāo)示一個(gè)文件 |
12 | Ecc, Yaffs_Tags本身的ECC校驗(yàn)和 |
2 | Unused |
其中Serial Number在文件系統(tǒng)創(chuàng)建時(shí)都為0,以后每次寫具有同一ObjectID和ChunkID的page的時(shí)候都加一,因?yàn)?/span>Yaffs在更新一個(gè)PAGE的時(shí)候總是在一個(gè)新的物理Page上寫入數(shù)據(jù),再將原先的物理Page刪除,所以該serial number可以在斷電等特殊情況下,當(dāng)新的page已經(jīng)寫入但老的page還沒有被刪除的時(shí)候用來識別正確的Page,保證數(shù)據(jù)的正確性。
ObjectID號為18bit,所以文件的總數(shù)限制在256K即26萬個(gè)左右。
最后以上這些是針對Yaffs1而言,對于Yaffs2因?yàn)獒槍?/span>chunk size大于1k的NAND FLASH,在Tags各分量及總體尺寸上都做了修改,以便更快更好的處理大容量的NAND FLASH芯片。由于Tag尺寸的增大,在512+16B類型的NAND FLASH上就一個(gè)Trunk對應(yīng)一個(gè)page的情況,目前就無法使用Yaffs2文件系統(tǒng)了。
由于文件系統(tǒng)的基本組織信息保存在頁面的備份空間中,因此,在文件系統(tǒng)加載時(shí)只需要掃描各個(gè)頁面的備份空間,即可建立起整個(gè)文件系統(tǒng)的結(jié)構(gòu),而不需要像JFFS1/2 那樣掃描整個(gè)介質(zhì),從而大大加快了文件系統(tǒng)的加載速度。
操作文件系統(tǒng)的第一步自然是取得SuperBlock了,Yaffs文件系統(tǒng)本身在NAND Flash上并不存在所謂的SuperBlock塊,完全是在文件系統(tǒng)mount的過程中由read_super函數(shù)填充的,不過有意思的一點(diǎn)是,由于物理上沒有存儲superblock塊,所以NAND Flash上的yaffs文件系統(tǒng)本身沒有存儲filesystem的魔數(shù)(MagicNum),在內(nèi)存中superblock里的s_magic參數(shù)也是直接賦值的,所以存儲在NAND FLASH上的任何文件系統(tǒng)都能被當(dāng)作yaffs文件系統(tǒng)mount上來,只是數(shù)據(jù)都會被當(dāng)作錯(cuò)誤數(shù)據(jù)放在lost+found目錄中,不知道這算不算yaffs文件系統(tǒng)的一個(gè)bug。
通常一個(gè)具體的文件系統(tǒng)在VFS的Super_block結(jié)構(gòu)中除了通用的數(shù)據(jù)外,還有自己專用的數(shù)據(jù),Yaffs文件系統(tǒng)的專用數(shù)據(jù)是一個(gè)yaffs_DeviceStruct結(jié)構(gòu),主要用來存儲一些相關(guān)軟硬件配置信息,相關(guān)函數(shù)指針和統(tǒng)計(jì)信息等。
在mount過程執(zhí)行read_super的過程中,Yaffs文件系統(tǒng)還需要將文件系統(tǒng)的目錄結(jié)構(gòu)在內(nèi)存中建立起來。由于沒有super塊,所以需要掃描Yaffs分區(qū),根據(jù)從OOB中讀取出的yaffs_tags信息判斷出是文件頭page還是數(shù)據(jù)page。再根據(jù)文件頭page中的內(nèi)容以及數(shù)據(jù)page中的ObjectID/ChunkID/serial Number等信息在內(nèi)存中為每個(gè)文件(Object)建立一個(gè)對應(yīng)的yaffs_object對象。
在yaffs_object結(jié)構(gòu)中,主要包含了:
Ø 如修改時(shí)間,用戶ID,組ID等文件屬性;
Ø 用作yaffs文件系統(tǒng)維護(hù)用的各種標(biāo)記位如臟(dirty)標(biāo)記,刪除標(biāo)記等等;
Ø 用作組織結(jié)構(gòu)的,如指向父目錄的Parent指針,指向同級目錄中其他對象鏈表的siblings雙向鏈表頭結(jié)構(gòu)
此外根據(jù)Object類型的不同(目錄,文件,鏈接),對應(yīng)于某一具體類型的Object,在Yaffs_object中還有其各自專有的數(shù)據(jù)內(nèi)容
Ø 普通文件:文件尺寸,用于快速查找文件數(shù)據(jù)塊的yaffs_Tnode 樹的指針等
Ø 目錄:目錄項(xiàng)內(nèi)容雙向鏈表頭(children)
Ø 鏈接:softlink的alias,hardlink對應(yīng)的ObjectID
除了對應(yīng)于存儲在NAND FLASH上的object而建立起來的yaffs_object以外,在read_super執(zhí)行過程中還會建立一些虛擬對象(Fake Object),這些Fake Object在NAND FLASH上沒有對應(yīng)的物理實(shí)體,比如在建立文件目錄結(jié)構(gòu)的最初,yaffs會建立四個(gè)虛擬目錄(Fake Directory):rootDir, unlinkedDir, deleteDir, lostNfoundDir分別用作根目錄,unlinked對象掛接的目錄,delete對象掛接的目錄,無效或零時(shí)數(shù)據(jù)塊掛接的目錄。
通過創(chuàng)建這些yaffs_object,yaffs文件系統(tǒng)就能夠?qū)⒋鎯υ?/span>NAND FLASH上數(shù)據(jù)系統(tǒng)的組織起來,在內(nèi)存中維護(hù)一個(gè)完整的文件系統(tǒng)結(jié)構(gòu)。
這里所謂移植,就是在特定的軟硬件環(huán)境里編譯出yaffs文件系統(tǒng)模塊了。目前最新的yaffs版本的代碼里主要是按照2.6內(nèi)核的方式寫的Kconfig和Makefile,對于2.4內(nèi)核來說,改起來也很簡單,基本上,只需要:
Ø 在內(nèi)核中建立YAFFS目錄fs/yaffs,并把下載的YAFFS代碼復(fù)制到該目錄下面。
Ø 參考yaffs代碼中的Kconfig文件,按照2.4內(nèi)核的風(fēng)格修改你自己的Config.in文件,使得可以配置YAFFS。
Ø 修改fs/makefile,加入yaffs目錄
Ø 按照2.4內(nèi)核的風(fēng)格修改YAFFS目錄中的Makefile文件。
只是在配置YAFFS的時(shí)候需要注意一點(diǎn),即使你的NAND FLASH是512+16B的,不需要使用YAFFS2,也需要將對2k page的NAND FLASH的支持這一項(xiàng)選上,否則編譯無法通過(因?yàn)椴糠执a沒有用CONFIG宏包起來),不知道這是不是我下載的這個(gè)版本的個(gè)別現(xiàn)象,還是對Makefile還需要進(jìn)一步的修改。
此外就是最好把Lets Yaffs do its own ECC選上,理由后面會說,其他選項(xiàng)就無所謂了,主要是對性能的調(diào)整,看著選吧,按推薦配置好了,比如Turn off debug chunk erase check,這一項(xiàng),我試驗(yàn)的結(jié)果選上后平均可以提高20-30%左右的擦寫速度。
Yaffs源代碼包的utils目錄下包含了mkyaffsimage/mkyaffs2image的代碼,簡單的修改一下Makefile里的內(nèi)核路徑就能編譯出mkyaffsimage/mkyaffs2image工具。
運(yùn)行mkyaffsimage dir imagename可以制作出yaffs1文件系統(tǒng)的鏡像。
但是,需要注意的是,制作出來的yaffs image文件與通常的文件系統(tǒng)的image文件不同,因?yàn)樵?/span>image文件里除了以512字節(jié)為單位的一個(gè)page的data數(shù)據(jù)外,同時(shí)緊跟在后還包括了16字節(jié)為單位的NAND備份數(shù)據(jù)區(qū)(OOB)的數(shù)據(jù)。所以實(shí)際上是以528個(gè)字節(jié)為單位的。就是因?yàn)榘诉@額外的16字節(jié)/page的數(shù)據(jù),所以基本上常規(guī)辦法如dd,或者通常的下載其它類型image的工具就無法正常下載yaffs image了,需要修改你所使用的下載工具的代碼,使得它能將yaffs image中的這些額外數(shù)據(jù)也寫入NAND FLASH OOB中。
這里還有一點(diǎn)需要注意的是,通過mkyaffsimage制做出來的image其OOB中也包含它自己計(jì)算的ECC校驗(yàn)數(shù)據(jù),其校驗(yàn)算法有可能和MTD NAND驅(qū)動(dòng)的校驗(yàn)算法不同,如果在內(nèi)核中由MTD來處理ECC,會造成MTD認(rèn)為所有的page都校驗(yàn)錯(cuò)誤。所以,這也是我前面說最好把Lets Yaffs do its own ECC選上的原因,同時(shí),要把MTD NAND驅(qū)動(dòng)中的ECC校驗(yàn)關(guān)閉。
如果不考慮產(chǎn)線批量下載的話,也可以通過mount拷貝的方式準(zhǔn)備yaffs文件系統(tǒng)。用flash_eraseall將NAND FLASH分區(qū)擦除,然后做為yaffs分區(qū)直接mount上來,將文件系統(tǒng)的內(nèi)容拷貝上去就可以了。這可能是在真正的NAND FLASH上試驗(yàn)yaffs文件系統(tǒng)最簡單的方式了。
沒有相應(yīng)的NAND FLASH設(shè)備包含兩種情況:
Ø 硬件上沒有NAND FLASH,開發(fā)板上沒有或者想在主機(jī)環(huán)境中測試yaffs文件系統(tǒng)
Ø 沒有合適的page size的NAND FLASH芯片,比如板上NAND FLASH芯片為512+16的格式,但是想要試驗(yàn)Yaffs2文件系統(tǒng)。
Yaffs提供了兩種用來在這種情況下測試yaffs文件系統(tǒng)的途徑。
Yaffs source包里包含了mtdemul目錄,Yaffs2中該目錄下的文件主要是Nandemul2k.c用來模擬2K page size的NAND FLASH。在Yaffs中則是Nandemul.c用來模擬512字節(jié)page size的NAND FLASH。
稍微修改一下Makeflie將編譯出來的模塊插入內(nèi)核,將在/dev/mtd /dev/mtdblock目錄下創(chuàng)建一個(gè)新的MTD設(shè)備。然后就可以將該設(shè)備當(dāng)作一個(gè)物理的MTD NAND設(shè)備分區(qū)進(jìn)行相關(guān)的操作,可以在上面創(chuàng)建yaffs文件系統(tǒng),mount umount等等。這種方法不僅適用于yaffs文件系統(tǒng),同樣也適用于其它可用于NAND設(shè)備的文件系統(tǒng)。
根據(jù)yaffs 官方文檔的描述,通過mount –t yaffsram none /mountpoint 可以在內(nèi)存中建立一個(gè)yaffs分區(qū),這有些類似于ramfs。不過,在試驗(yàn)最新版本的Yaffs2文件系統(tǒng)時(shí),該功能并不可用,只有Yaffs1文件系統(tǒng)的代碼包里包含了相關(guān)的代碼。