JFFS2 文件系統(tǒng)及新特性介紹
掃描二維碼
隨時(shí)隨地手機(jī)看文章
JFFS2 是一個(gè)開(kāi)放源碼的項(xiàng)目(www.infradead.org)。 它是在閃存上使用非常廣泛的讀/寫(xiě)文件系統(tǒng),在嵌入式系統(tǒng)中被普遍的應(yīng)用。這篇文章首先分析了在閃存上使用 JFFS2 的必要性,然后詳細(xì)的闡述了 JFFS2 實(shí)現(xiàn)的內(nèi)部機(jī)制,包括日志結(jié)構(gòu)的文件系統(tǒng),關(guān)鍵的數(shù)據(jù)結(jié)構(gòu),掛載過(guò)程和垃圾收集機(jī)制。同時(shí)也指出了 JFFS2 的局限性,并介紹了最新的針對(duì) JFFS2 的不足進(jìn)行改進(jìn)的補(bǔ)丁程序。最后對(duì) JFFS3 的設(shè)計(jì)思想和現(xiàn)在的開(kāi)發(fā)狀況給予了簡(jiǎn)單的介紹。
1. 為什么需要 JFFS2
這一小節(jié)首先介紹了閃存相對(duì)于磁盤(pán)介質(zhì)的特別之處,然后分析了將磁盤(pán)文件系統(tǒng)運(yùn)行在閃存上的不足,同時(shí)也給出了我們使用 JFFS2 的理由。
1.1 閃存(Flash Memory) 的特性和限制
這里所介紹的閃存的特性和限制都是從上層的文件系統(tǒng)的角度來(lái)看的,而不會(huì)涉及到具體的物理特性??偟膩?lái)說(shuō),有兩種類型的 flash memory: NOR flash 和 NAND flash. 先介紹一下這兩種閃存所具有的共同特性。
A) 閃存的最小尋址單位是字節(jié)(byte),而不是磁盤(pán)上的扇區(qū)(sector)。這意味著我們可以從一塊閃存的任意偏移(offset)讀數(shù)據(jù),但并不表明對(duì)閃存寫(xiě)操作也是以字節(jié)為單位進(jìn)行的。我們會(huì)在下面的闡述中找到答案。
B) 當(dāng)一塊閃存處在干凈的狀態(tài)時(shí)(被擦寫(xiě)過(guò),但是還沒(méi)有寫(xiě)操作發(fā)生),在這塊flash上的每一位(bit)都是邏輯1。
C) 閃存上的每一位(bit)可以被寫(xiě)操作置成邏輯0。 可是把邏輯 0 置成邏輯 1 卻不能按位(bit)來(lái)操作,而只能按擦寫(xiě)塊(erase block)為單位進(jìn)行擦寫(xiě)操作。擦寫(xiě)塊的大小從 4K 到128K 不等。從上層來(lái)看,擦寫(xiě)所完成的功能就是把擦寫(xiě)塊內(nèi)的每一位都重設(shè)置(reset)成邏輯 1。
D) 閃存的使用壽命是有限的。具體來(lái)說(shuō),閃存的使用壽命是由擦寫(xiě)塊的最大可擦寫(xiě)次數(shù)來(lái)決定的。超過(guò)了最大可擦寫(xiě)次數(shù),這個(gè)擦寫(xiě)塊就成為壞塊(bad block)了。因此為了避免某個(gè)擦寫(xiě)塊被過(guò)度擦寫(xiě),以至于它先于其他的擦寫(xiě)塊達(dá)到最大可擦寫(xiě)次數(shù),我們應(yīng)該在盡量小的影響性能的前提下,使擦寫(xiě)操作均勻的分布在每個(gè)擦寫(xiě)塊上。這個(gè)過(guò)程叫做磨損平衡(wear leveling)。
NOR flash 與 NAND flash 的不同之處:
A) NOR flash 讀/寫(xiě)操作的基本單位是字節(jié);而 NAND flash 又把擦寫(xiě)塊分成頁(yè)(page), 頁(yè)是寫(xiě)操作的基本單位,一般一個(gè)頁(yè)的大小是 512 或 2K 個(gè)字節(jié)。對(duì)于一個(gè)頁(yè)的重復(fù)寫(xiě)操作次數(shù)是有限制的,不同廠商生產(chǎn)的 NAND flash 有不同的限制,有些是一次,有些是四次,六次或十次。
B) 按照現(xiàn)在的技術(shù)水平,一般來(lái)說(shuō)NOR flash擦寫(xiě)塊的最大可擦寫(xiě)次數(shù)在十萬(wàn)次左右,NAND flash擦寫(xiě)塊的最大可擦寫(xiě)次數(shù)在百萬(wàn)次左右。
1.2 閃存轉(zhuǎn)換層
將磁盤(pán)文件系統(tǒng)(ext2, FAT)運(yùn)行在閃存上的很自然的方法就是在文件系統(tǒng)和閃存之間提供一個(gè)閃存轉(zhuǎn)換層(Flash Translation Layer), 它的功能就是將底層的閃存模擬成一個(gè)具有 512字節(jié)扇區(qū)大小的標(biāo)準(zhǔn)塊設(shè)備(block device)。對(duì)于文件系統(tǒng)來(lái)說(shuō),就像工作在一個(gè)普通的塊設(shè)備上一樣,沒(méi)有任何的差別。
圖一
一個(gè)閃存轉(zhuǎn)換層的最簡(jiǎn)單的實(shí)現(xiàn)就是將模擬的塊設(shè)備一對(duì)一的映射到閃存上。舉例來(lái)說(shuō),當(dāng)上層的文件系統(tǒng)要寫(xiě)一個(gè)塊設(shè)備的扇區(qū)時(shí),閃存轉(zhuǎn)換層要做下面的操作來(lái)完成這個(gè)寫(xiě)請(qǐng)求:
1 將這個(gè)扇區(qū)所在擦寫(xiě)塊地?cái)?shù)據(jù)讀到內(nèi)存中,放在緩存(buffer)中
2 將緩存中與這個(gè)扇區(qū)對(duì)應(yīng)的內(nèi)容用新的內(nèi)容替換掉
3 對(duì)該擦寫(xiě)塊執(zhí)行擦寫(xiě)操作
4 將緩沖中的數(shù)據(jù)寫(xiě)回該擦寫(xiě)塊
這種實(shí)現(xiàn)方式的缺點(diǎn)是很明顯的:
1 效率低,對(duì)一個(gè)扇區(qū)的更新要重寫(xiě)整個(gè)擦寫(xiě)塊上的數(shù)據(jù),造成數(shù)據(jù)帶寬很大的浪費(fèi)。
2 沒(méi)有提供磨損平衡,那些被頻繁更新的數(shù)據(jù)所在擦寫(xiě)塊將首先變成壞塊。
3 非常不安全,很容易引起數(shù)據(jù)的丟失。如果在上面的第三步和第四步之間發(fā)生了突然掉電(power loss),那么整個(gè)擦寫(xiě)塊中的數(shù)據(jù)就全部丟失了。這在突然掉電經(jīng)常發(fā)生的嵌入式系統(tǒng)中是不能接受的。
MTD 中的內(nèi)核模塊 mtdblock 就是基于這種機(jī)制實(shí)現(xiàn)的,同時(shí)還作了一些優(yōu)化。只有當(dāng)文件系統(tǒng)的寫(xiě)請(qǐng)求超過(guò)了一個(gè)擦寫(xiě)塊的邊界的時(shí)候,它才會(huì)執(zhí)行對(duì)閃存的擦寫(xiě),寫(xiě)回操作。
因此,為了解決上面這種實(shí)現(xiàn)方式的問(wèn)題,閃存轉(zhuǎn)換層需要做更多的事情。閃存轉(zhuǎn)換層不能只實(shí)現(xiàn)這種一對(duì)一的映射,而需要將模擬塊設(shè)備的扇區(qū)存儲(chǔ)在閃存的不同位置,并且維持扇區(qū)到閃存的映射關(guān)系。更進(jìn)一步,閃存轉(zhuǎn)換層還必須能理解上層文件系統(tǒng)的語(yǔ)義,否則閃存轉(zhuǎn)換層沒(méi)辦法做垃圾回收(Garbage Collection)。這樣實(shí)現(xiàn)最大的問(wèn)題就是效率不高,具體來(lái)說(shuō),閃存轉(zhuǎn)換層為了能理解上層文件系統(tǒng)的語(yǔ)義,必須對(duì)文件系統(tǒng)的每個(gè)寫(xiě)請(qǐng)求進(jìn)行解析,這勢(shì)必帶來(lái)寫(xiě)操作性能的下降。另外要求文件系統(tǒng)下面的一層去理解文件系統(tǒng)的語(yǔ)義,很顯然這不是最好的解決方式。我們還有很好的解決問(wèn)題的方法,就是實(shí)現(xiàn)一個(gè)特別針對(duì)閃存的文件系統(tǒng)。而 JFFS2 就是一個(gè)這樣的文件系統(tǒng)。
2. JFFS2
有 JFFS2 就要有 JFFS v1,沒(méi)錯(cuò),JFFS v1 最初是由瑞典的 Axis Communications AB 公司開(kāi)發(fā)的,使用在他們的嵌入式設(shè)備中,并且在 1999 年末基于 GNU GPL 發(fā)布出來(lái)。最初的發(fā)布版本基于 Linux 內(nèi)核 2.0,后來(lái) RedHat 將它移植到 Linux 內(nèi)核 2.2,做了大量的測(cè)試和 bug fix 的工作使它穩(wěn)定下來(lái),并且對(duì)簽約客戶提供商業(yè)支持。但是在使用的過(guò)程中,JFFS v1 設(shè)計(jì)中的局限被不斷的暴露出來(lái)。于是在 2001 年初的時(shí)候,RedHat 決定實(shí)現(xiàn)一個(gè)新的閃存文件系統(tǒng),這就是現(xiàn)在的 JFFS2。下面將詳細(xì)介紹 JFFS2 設(shè)計(jì)中主要的思想,關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)和垃圾收集機(jī)制。這將為我們實(shí)現(xiàn)一個(gè)閃存上的文件系統(tǒng)提供很好的啟示。首先,JFFS2 是一個(gè)日志結(jié)構(gòu)(log-structured)的文件系統(tǒng),包含數(shù)據(jù)和原數(shù)據(jù)(meta-data)的節(jié)點(diǎn)在閃存上順序的存儲(chǔ)。JFFS2 之所以選擇日志結(jié)構(gòu)的存儲(chǔ)方式,是因?yàn)閷?duì)閃存的更新應(yīng)該是 out-of-place 的更新方式,而不是對(duì)磁盤(pán)的 in-place 的更新方式。在閃存上 in-place 更新方式的問(wèn)題我們已經(jīng)在閃存轉(zhuǎn)換層一節(jié)描述過(guò)了。
2.1 節(jié)點(diǎn)頭部定義和兼容性
JFFS2 將文件系統(tǒng)的數(shù)據(jù)和原數(shù)據(jù)以節(jié)點(diǎn)的形式存儲(chǔ)在閃存上,具體來(lái)說(shuō)節(jié)點(diǎn)頭部的定義如下:
圖二
幻數(shù)屏蔽位:0x1985 用來(lái)標(biāo)識(shí) JFFS2 文件系統(tǒng)。
節(jié)點(diǎn)類型:JFFS2 自身定義了三種節(jié)點(diǎn)類型,但是考慮到文件系統(tǒng)可擴(kuò)展性和兼容性,JFFS2從 ext2 借鑒了經(jīng)驗(yàn),節(jié)點(diǎn)類型的最高兩位被用來(lái)定義節(jié)點(diǎn)的兼容屬性,具體來(lái)說(shuō)有下面幾種兼容屬性:
JFFS2_FEATURE_INCOMPAT:當(dāng) JFFS2 發(fā)現(xiàn)了一個(gè)不能識(shí)別的節(jié)點(diǎn)類型,并且它的兼容屬性是 JFFS2_FEATURE_INCOMPAT,那么 JFFS2 必須拒絕掛載(mount)文件系統(tǒng)。
JFFS2_FEATURE_ROCOMPAT:當(dāng) JFFS2 發(fā)現(xiàn)了一個(gè)不能識(shí)別的節(jié)點(diǎn)類型,并且它的兼容屬性是 JFFS2_FEATURE_ROCOMPAT,那么 JFFS2 必須以只讀的方式掛載文件系統(tǒng)。
JFFS2_FEATURE_RWCOMPAT_DELETE:當(dāng) JFFS2 發(fā)現(xiàn)了一個(gè)不能識(shí)別的節(jié)點(diǎn)類型,并且它的兼容屬性是 JFFS2_FEATURE_RWCOMPAT_DELETE,那么在垃圾回收的時(shí)候,這個(gè)節(jié)點(diǎn)可以被刪除。
JFFS2_FEATURE_RWCOMPAT_COPY:當(dāng) JFFS2 發(fā)現(xiàn)了一個(gè)不能識(shí)別的節(jié)點(diǎn)類型,并且它的兼容屬性是 JFFS2_FEATURE_RWCOMPAT_COPY,那么在垃圾回收的時(shí)候,這個(gè)節(jié)點(diǎn)要被拷貝到新的位置。
節(jié)點(diǎn)總長(zhǎng)度:包括節(jié)點(diǎn)頭和數(shù)據(jù)的長(zhǎng)度。
節(jié)點(diǎn)頭部 CRC 校驗(yàn):包含節(jié)點(diǎn)頭部的校驗(yàn)碼,為文件系統(tǒng)的可靠性提供了支持。
2.2 節(jié)點(diǎn)類型
JFFS2 定義了三種節(jié)點(diǎn)類型:
JFFS2_NODETYPE_INODE: INODE 節(jié)點(diǎn)包含了i-節(jié)點(diǎn)的原數(shù)據(jù)(i節(jié)點(diǎn)號(hào),文件的組 ID, 屬主 id, 訪問(wèn)時(shí)間,偏移,長(zhǎng)度等),文件數(shù)據(jù)被附在 INODE 節(jié)點(diǎn)之后。除此之外,每個(gè) INODE 節(jié)點(diǎn)還有一個(gè)版本號(hào),它被用來(lái)維護(hù)屬于一個(gè)i-節(jié)點(diǎn)的所有 INODE 節(jié)點(diǎn)的全序關(guān)系。下面舉例來(lái)說(shuō)明這個(gè)全序關(guān)系在 JFFS2 的使用:
圖三
因此,當(dāng)文件系統(tǒng)從閃存上讀節(jié)點(diǎn)信息后,會(huì)生成下面的映射信息:
圖四
根據(jù)這個(gè)映射信息表,文件系統(tǒng)就知道到相應(yīng)的 INODE 節(jié)點(diǎn)去讀取相應(yīng)的文件內(nèi)容。最后要說(shuō)明的是,JFFS2 支持文件數(shù)據(jù)的壓縮存儲(chǔ),因此在 INODE 節(jié)點(diǎn)中還包含了所使用的壓縮算法,在讀取數(shù)據(jù)的時(shí)候選擇相應(yīng)的壓縮算法來(lái)解壓縮。
JFFS2_NODETYPE_DIRENT:DIRENT 節(jié)點(diǎn)就是把文件名與 i 節(jié)點(diǎn)對(duì)應(yīng)起來(lái)。在 DIRENT節(jié)點(diǎn)中也有一個(gè)版本號(hào),這個(gè)版本號(hào)的作用主要是用來(lái)刪除一個(gè) dentry。具體來(lái)說(shuō),當(dāng)我們要從一個(gè)目錄中刪除一個(gè) dentry 時(shí),我們要寫(xiě)一個(gè) DIRENT 節(jié)點(diǎn),節(jié)點(diǎn)中的文件名與被刪除的 dentry 中的文件名相同,i 節(jié)點(diǎn)號(hào)置為 0,同時(shí)設(shè)置一個(gè)更高的版本號(hào)。
JFFS2_NODETYPE_CLEANMARKER:當(dāng)一個(gè)擦寫(xiě)塊被擦寫(xiě)完畢后,CLEANMARKER 節(jié)點(diǎn)會(huì)被寫(xiě)在 NOR flash 的開(kāi)頭,或 NAND flash 的 OOB(Out-Of-Band) 區(qū)域來(lái)表明這是一個(gè)干凈,可寫(xiě)的擦寫(xiě)塊。在 JFFS v1 中,如果掃描到開(kāi)頭的 1K 都是 0xFF 就認(rèn)為這個(gè)擦寫(xiě)塊是干凈的。但是在實(shí)際的測(cè)試中發(fā)現(xiàn),如果在擦寫(xiě)的過(guò)程中突然掉電,擦寫(xiě)塊上也可能會(huì)有大塊連續(xù) 0xFF,但是這并不表明這個(gè)擦寫(xiě)塊是干凈的。于是我們需要 CLEANMARKER 節(jié)點(diǎn)來(lái)確切的標(biāo)識(shí)一個(gè)干凈的擦寫(xiě)塊。
2.3 JFFS2節(jié)點(diǎn),擦寫(xiě)塊在內(nèi)存中的表示和操作
JFFS2 維護(hù)了幾個(gè)鏈表來(lái)管理擦寫(xiě)塊,根據(jù)擦寫(xiě)塊上的內(nèi)容,一個(gè)擦寫(xiě)塊會(huì)在不同的鏈表上。具體來(lái)說(shuō),當(dāng)一個(gè)擦寫(xiě)塊上都是合法(valid)的節(jié)點(diǎn)時(shí),它會(huì)在 clean_list 上;當(dāng)一個(gè)擦寫(xiě)塊包含至少一個(gè)過(guò)時(shí)(obsolete)的節(jié)點(diǎn)時(shí),它會(huì)在 dirty_list 上;當(dāng)一個(gè)擦寫(xiě)塊被擦寫(xiě)完畢,并被寫(xiě)入 CLEANMARKER 節(jié)點(diǎn)后,它會(huì)在 free_list 上。
通常情況下,JFFS2 順序的在擦寫(xiě)塊上寫(xiě)入不同的節(jié)點(diǎn),直到一個(gè)擦寫(xiě)塊被寫(xiě)滿。此時(shí) JFFS2 從 free_list 上取下一個(gè)擦寫(xiě)塊,繼續(xù)從擦寫(xiě)塊的開(kāi)頭開(kāi)始寫(xiě)入節(jié)點(diǎn)。當(dāng) free_list 上擦寫(xiě)塊的數(shù)量逐漸減少到一個(gè)預(yù)先設(shè)定的閥值的時(shí)候,垃圾回收就被觸發(fā)了,為文件系統(tǒng)清理出更多的可用擦寫(xiě)塊。為了減少對(duì)內(nèi)存的占用,JFFS2 并沒(méi)有把 i 節(jié)點(diǎn)所有的信息都保留在內(nèi)存中,而只是把那些在請(qǐng)求到來(lái)時(shí)不能很快獲得的信息保留在內(nèi)存中。具體來(lái)說(shuō),對(duì)于在閃存上的每個(gè) i 節(jié)點(diǎn),在內(nèi)存里都有一個(gè) struct jffs2_inode_cache 與之對(duì)應(yīng),這個(gè)結(jié)構(gòu)里保存了 i 節(jié)點(diǎn)號(hào),指向 i 節(jié)點(diǎn)的連接數(shù),以及一個(gè)指向?qū)儆谶@個(gè) i 節(jié)點(diǎn)的物理節(jié)點(diǎn)鏈表的指針。所有的 struct jffs2_inode_cache 存儲(chǔ)在一個(gè)哈希表中。閃存上的每個(gè)節(jié)點(diǎn)在內(nèi)存中由一個(gè) struct jffs2_raw_node_ref 表示,這個(gè)結(jié)構(gòu)里保存了此節(jié)點(diǎn)的物理偏移,總長(zhǎng)度,以及兩個(gè)指向 struct jffs2_raw_node_ref 的指針。一個(gè)指針指向此節(jié)點(diǎn)在物理擦寫(xiě)塊上的下一個(gè)節(jié)點(diǎn),另一個(gè)指針指向?qū)儆谕粋€(gè) i-節(jié)點(diǎn)的物理節(jié)點(diǎn)鏈表的下一個(gè)節(jié)點(diǎn)。
圖五
在閃存上的節(jié)點(diǎn)的起始偏移都是 4 字節(jié)對(duì)齊的,所以 struct jffs2_inode_cache 中flash_offset 的最低兩位沒(méi)有被用到。JFFS2 正好利用最低位作為此節(jié)點(diǎn)是否過(guò)時(shí)的標(biāo)記。
下面舉一例來(lái)說(shuō)明 JFFS2 是如何使用這些數(shù)據(jù)結(jié)構(gòu)的。VFS 調(diào)用 iget() 來(lái)得到一個(gè) i 節(jié)點(diǎn)的信息,當(dāng)這個(gè) i 節(jié)點(diǎn)不在緩存中的時(shí)候,VFS 就會(huì)調(diào)用 JFFS2 的 read_inode() 回調(diào)函數(shù)來(lái)得到 i 節(jié)點(diǎn)信息。傳給 read_inode() 的參數(shù)是 i 節(jié)點(diǎn)號(hào),JFFS2 用這個(gè) i 節(jié)點(diǎn)號(hào)從哈希表中查找相應(yīng)的 struct jffs2_inode_cache,然后利用屬于這個(gè) i 節(jié)點(diǎn)的節(jié)點(diǎn)鏈表從閃存上讀入節(jié)點(diǎn)信息,建立類似于表三的映射信息。
2.4 JFFS2 掛載過(guò)程
JFFS2 的掛載過(guò)程分為四個(gè)階段:
1) JFFS2 掃描閃存介質(zhì),檢查每個(gè)節(jié)點(diǎn) CRC 校驗(yàn)碼的合法性,同時(shí)分配了 struct jffs2_inode_cache 和 struct jffs2_raw_node_ref
2) 掃描每個(gè) i 節(jié)點(diǎn)的物理節(jié)點(diǎn)鏈表,標(biāo)識(shí)出過(guò)時(shí)的物理節(jié)點(diǎn);對(duì)每一個(gè)合法的 dentry 節(jié)點(diǎn),將相應(yīng)的 jffs2_inode_cache 中的 nlink 加一。
3 找出 nlink 為 0 的 jffs2_inode_cache,釋放相應(yīng)的節(jié)點(diǎn)。
4 釋放在掃描過(guò)程中使用的臨時(shí)信息。
2.5 JFFS2 垃圾回收機(jī)制
當(dāng) free_list 上的擦寫(xiě)塊數(shù)太少了,垃圾回收就會(huì)被觸發(fā)。垃圾回收主要的任務(wù)就是回收那些已經(jīng)過(guò)時(shí)的節(jié)點(diǎn),但是除此之外它還要考慮磨損平衡的問(wèn)題。因?yàn)槿绻晃兜膹?dirty_list上選取擦寫(xiě)塊進(jìn)行垃圾回收,那么 dirty_list 上的擦寫(xiě)塊將先于 clean_list 上的擦寫(xiě)塊被磨損壞。JFFS2 的處理方式是以 99% 的概率從 dirty_list,1% 的概率從 clean_list 上取一個(gè)擦寫(xiě)塊下來(lái)。由此可以看出 JFFS2 的設(shè)計(jì)思想是偏向于性能,同時(shí)兼顧磨損平衡。對(duì)這個(gè)塊上每一個(gè)沒(méi)有過(guò)時(shí)的節(jié)點(diǎn)執(zhí)行相同的操作:
1 找出這個(gè)節(jié)點(diǎn)所屬的 i 節(jié)點(diǎn)號(hào)(見(jiàn)圖五)。
2 調(diào)用 iget(),建立這個(gè) i 節(jié)點(diǎn)的文件映射表。
3 找出這個(gè)節(jié)點(diǎn)上沒(méi)有過(guò)時(shí)的數(shù)據(jù)內(nèi)容,并且如果合法的數(shù)據(jù)太少,JFFS2 還會(huì)合并相鄰的節(jié)點(diǎn)。
4 將數(shù)據(jù)讀入倒緩存里,然后將它拷貝到新的擦寫(xiě)塊上。
5 將回收的節(jié)點(diǎn)置為過(guò)時(shí)。
當(dāng)擦寫(xiě)塊上所有的節(jié)點(diǎn)都被置為過(guò)時(shí),就可以擦寫(xiě)這個(gè)擦寫(xiě)塊,回收使用它。
3. JFFS2 的不足之處
3.1 掛載時(shí)間過(guò)長(zhǎng)
JFFS2 的掛載過(guò)程需要對(duì)閃存從頭到尾的掃描,這個(gè)過(guò)程是很慢的,我們?cè)跍y(cè)試中發(fā)現(xiàn),掛載一個(gè) 16M 的閃存有時(shí)需要半分鐘以上的時(shí)間。
3.2 磨損平衡的隨意性(random nature)
JFFS2 對(duì)磨損平衡是用概率的方法來(lái)解決的,這很難保證磨損平衡的確定性。在某些情況下,可能造成對(duì)擦寫(xiě)塊不必要的擦寫(xiě)操作;在某些情況下,又會(huì)引起對(duì)磨損平衡調(diào)整的不及時(shí)。
3.3 很差的擴(kuò)展性
JFFS2 中有兩個(gè)地方的處理是 O(N) 的,這使得它的擴(kuò)展性很差。
首先,掛載時(shí)間同閃存的大小,閃存上節(jié)點(diǎn)數(shù)目成正比。
其次,雖然 JFFS2 盡可能的減少內(nèi)存的占用,但通過(guò)上面對(duì) JFFS2 的介紹我們可以知道實(shí)際上它對(duì)內(nèi)存的占用量是同 i 節(jié)點(diǎn)數(shù)和閃存上的節(jié)點(diǎn)數(shù)成正比的。
因此在實(shí)際應(yīng)用中,JFFS2 最大能用在 128M 的閃存上。
4. JFFS2 的新特性
最近加入到 JFFS2 中的兩個(gè)補(bǔ)丁程序分別解決了上面提到的掛載時(shí)間過(guò)長(zhǎng)和磨損平衡隨意性的問(wèn)題。
4.1 磨損塊小結(jié)補(bǔ)丁程序(erase block summary patch)
這個(gè)補(bǔ)丁程序最基本的思想就是用空間來(lái)?yè)Q時(shí)間。具體來(lái)說(shuō),就是將每個(gè)擦寫(xiě)塊每個(gè)節(jié)點(diǎn)的原數(shù)據(jù)信息寫(xiě)在這個(gè)擦寫(xiě)塊的最后,當(dāng) JFFS2 掛載的時(shí)候,對(duì)每個(gè)擦寫(xiě)塊只需要讀一次來(lái)讀取這個(gè)小結(jié)節(jié)點(diǎn),因此大大減少了掛載時(shí)間。使用了磨損塊小結(jié)補(bǔ)丁程序,一個(gè)擦寫(xiě)塊的結(jié)構(gòu)就像下面這樣:
圖六
根據(jù)我們的測(cè)試,使用磨損塊小結(jié)補(bǔ)丁程序,掛載一個(gè) 12M 的閃存需要 2~3 秒,掛載一個(gè) 16M 的閃存需要 3~4 秒。
4.2 改進(jìn)的磨損平衡補(bǔ)丁程序
這個(gè)補(bǔ)丁程序的基本思想是,記錄每個(gè)擦寫(xiě)塊的擦寫(xiě)次數(shù),當(dāng)閃存上各個(gè)擦寫(xiě)塊的擦寫(xiě)次數(shù)的差距超過(guò)某個(gè)預(yù)定的閥值,開(kāi)始進(jìn)行磨損平衡的調(diào)整。調(diào)整的策略是,在垃圾回收時(shí)將擦寫(xiě)次數(shù)小的擦寫(xiě)塊上的數(shù)據(jù)遷移到擦寫(xiě)次數(shù)大的擦寫(xiě)塊上。這樣一來(lái)我們提高了磨損平衡的確定性,我們可以知道什么時(shí)候開(kāi)始磨損平衡的調(diào)整,也可以知道選取哪些擦寫(xiě)塊進(jìn)行磨損平衡的調(diào)整。
4.3 擦寫(xiě)塊頭部補(bǔ)丁程序
在寫(xiě)改進(jìn)的磨損平衡補(bǔ)丁程序的過(guò)程之中,我們需要記錄每個(gè)擦寫(xiě)塊的擦寫(xiě)次數(shù),這個(gè)信息需要記錄在各自的擦寫(xiě)塊上??墒俏覀儼l(fā)現(xiàn) JFFS2 中缺少一種靈活的對(duì)每個(gè)擦寫(xiě)塊的信息進(jìn)行擴(kuò)展的機(jī)制。于是我們?yōu)槊總€(gè)擦寫(xiě)塊引入了擦寫(xiě)塊頭部(header),這個(gè)頭部負(fù)責(zé)紀(jì)錄每個(gè)擦寫(xiě)塊的信息(比如說(shuō)擦寫(xiě)次數(shù)),并且它提供了靈活的擴(kuò)展機(jī)制,將來(lái)如果有新的信息需要記錄,可以很容易的加入到頭部之中。
5. JFFS3 簡(jiǎn)介
雖然不斷有新的補(bǔ)丁程序來(lái)提高 JFFS2 的性能,但是不可擴(kuò)展性是它最大的問(wèn)題,但是這是它自身設(shè)計(jì)的先天缺陷,是沒(méi)有辦法靠后天來(lái)彌補(bǔ)的。因此我們需要一個(gè)全新的文件系統(tǒng),而 JFFS3 就是這樣的一個(gè)文件系統(tǒng),JFFS3 的設(shè)計(jì)目標(biāo)是支持大容量閃存(>1TB)的文件系統(tǒng)。JFFS3 與 JFFS2 在設(shè)計(jì)上根本的區(qū)別在于,JFFS3 將索引信息存放在閃存上,而 JFFS2將索引信息保存在內(nèi)存中。比如說(shuō),由給定的文件內(nèi)的偏移定位到存儲(chǔ)介質(zhì)上的物理偏移地址所需的信息,查找某個(gè)目錄下所有的目錄項(xiàng)所需的信息都是索引信息的一種。 JFFS3 現(xiàn)在還處于設(shè)計(jì)階段,文件系統(tǒng)的基本結(jié)構(gòu)借鑒了 Reiser4 的設(shè)計(jì)思想,整個(gè)文件系統(tǒng)就是一個(gè) B+ 樹(shù)。JFFS3 的發(fā)起者正工作于垃圾回收機(jī)制的設(shè)計(jì),這是 JFFS3 中最復(fù)雜,也是最富有挑戰(zhàn)性的部分。JFFS3 的設(shè)計(jì)文檔可以在http://www.linux-mtd.infradead.org/doc/jffs3.html 得到,有興趣的讀者可以積極參與到 JFFS3 的設(shè)計(jì)中,發(fā)表自己的見(jiàn)解,參與討論。
致謝
在這里要特別感謝David Woodhouse, Artem B. Bityutskiy,Joern 和 Thomas Gleixner,在參與到JFFS2的開(kāi)發(fā)過(guò)程中,這幾位主要的項(xiàng)目維護(hù)者(maintainer)不斷地給我?guī)椭托牡幕卮鹞业膯?wèn)題,在與他們的討論過(guò)程中碰撞出很多智慧的火花和富有啟發(fā)性的思想,尤其是他們對(duì)我的補(bǔ)丁程序提出的”尖刻”的問(wèn)題,讓我不斷的努力思考(thinking hard),不斷的完善它們。我想這種開(kāi)放,無(wú)私,客觀的精神正是開(kāi)源社區(qū)的精髓所在,吸引著越來(lái)越多的開(kāi)發(fā)者參與進(jìn)來(lái),并使開(kāi)源社區(qū)不斷的壯大。