UBIFS文件系統(tǒng)設(shè)計
a brief introduction to the design of UBIFS
為flash memory設(shè)計的文件系統(tǒng)要求異地更新,。這是因為flash memory在可寫之前必須要先擦除,且擦除之前只能寫一次。如果eraseblocks很小且可以快速擦除,,那么可以將它們看作disk sector,,但是實際上不是那種情況。讀出一個整塊的eraseblock,,擦除它,,再回寫更新的的數(shù)據(jù),所花時間比單獨在其它已經(jīng)擦除了的不同的eraseblock更新數(shù)據(jù)長100倍,。換句話說,,對于一個小的updates,in-place比out-of-place更新時間長100倍,。 out-of-place更新要求垃圾回收,。當(dāng)有數(shù)據(jù)out-of-place更新時,原來的eraseblocks就包含了有效數(shù)據(jù)和絕對數(shù)據(jù)(在別的地方已經(jīng)更新),。這樣到最后,,文件系統(tǒng)將用完所有空的eraseblocks,以至于每個eraseblocks包括有效數(shù)據(jù)和絕對數(shù)據(jù),。為了在別的地方寫新的數(shù)據(jù),,必須有一個eraseblock是空的以至于可以擦除和重新使用。確認(rèn)包含許多絕對數(shù)據(jù)的eraseblock,,移動有效數(shù)據(jù)其它eraseblock,,這個過程叫垃圾回收(garbage collection)。 垃圾回收提議node-structure的好處,。為了能回收一塊eraseblock,,文件系統(tǒng)必須能確認(rèn)存儲在那的數(shù)據(jù)。這是文件系統(tǒng)面對的一般索引問題的矛盾,。文件系統(tǒng)通常以一個文件名開始和必須找到屬于這個文件的數(shù)據(jù),。垃圾回收可以開始于任何一個文件的數(shù)據(jù)且必須發(fā)現(xiàn)這個文件屬于哪個文件,。解決這個問題的一個方法是根據(jù)文件的數(shù)據(jù)存儲元數(shù)據(jù)(metadata)。數(shù)據(jù)和元數(shù)據(jù)的組合稱為一個節(jié)點(node),。每一個節(jié)點記錄著其屬于哪個文件和這個節(jié)點包含著什么數(shù)據(jù),。JIFFS2和UBIFS都是按照一個node-structured設(shè)計的,它使能它們的垃圾回收者直接讀eraseblocks,決定哪些data需要移動,,哪些要丟棄,,根據(jù)實際情況去改變索引。 JIFFS2和UBIFS之間最大的不同就是UBIFS將索引存儲在flash上,,而JIFFS2存儲在main memory中,,當(dāng)文件系統(tǒng)被掛載的時候,main memory將重新建立,。JIFFS2這樣就潛在地給自己最大尺寸放了一個限制,,因為掛載時間和memory使用隨著flash的大小線性增長。 UBIFS被特地的設(shè)計去克服這個限制,。 不幸的是,,存儲索引在flash上是非常復(fù)制的,因為所以本身必須異地更新,。當(dāng)索引的一部分被異地更新,,那么相關(guān)更新索引的任何其它部分也必須要被更新。然后,,依次地,,相關(guān)部分的部分必須被更新。這樣看起來好像永無止境地更新下去,,一個解決的辦法就是使用wandering tree,。 對于UBIFS wandering tree,只有樹上的葉子包含文件信息。他們是文件系統(tǒng)的有效節(jié)點,。樹的內(nèi)部元素是索引節(jié)點(index nodes)和包含相關(guān)的子節(jié)點,。也就是說,一個node記錄著它的子節(jié)點的位置,。所以UBIFS wandering tree可以看成兩個部分,。頂部分由創(chuàng)建樹結(jié)構(gòu)的索引節(jié)點組成,底部分由指向?qū)嶋H數(shù)據(jù)的leaf node組成,。頂部分可以簡單地看作索引index,。一個文件系統(tǒng)的更新過程由創(chuàng)建一個新的leaf node和添加node(或者代替原來的到wandering tree)組成。為了能做到,,父索引節(jié)點必須也要被代替,,如此,父節(jié)點的父節(jié)點,,一直到tree的根(root),,都必須要被代替,。要被代替的索引節(jié)點數(shù)等于樹的高度。這里留下的問題就是怎么知道tree的根在哪里,。在UBIFS中,,根索引節(jié)點的位置存儲在主節(jié)點(master node)里。 主節(jié)點存儲著所有flash上沒有固定邏輯位置的結(jié)構(gòu)的位置,。主節(jié)點自身被重復(fù)寫到logical eraseblocks(LEBs) one and two.LEBs是UBI創(chuàng)建的一種抽象,。UBI將physical eraseblocks(PEBs)映射到LEBs,所以LEBs one and two可以是flash介質(zhì)上的任何地方(嚴(yán)格來說是UBI設(shè)備),,無論如何,,UBI都記錄著他們在哪里。兩個eraseblocks被使用,,是為了保證有兩個master node的拷貝,。這個是為了恢復(fù)著想,因為有兩種情況會導(dǎo)致主節(jié)點損壞或丟失,。第一種情況就是當(dāng)master node正在被寫入的時候突然斷電,;第二種情況是可能是flash介質(zhì)自身損壞。對于第一種情況恢復(fù)是可以的因為先前的主節(jié)點還可以使用,。對于第二種情況恢復(fù)是不可能的因為無法確定哪一個主節(jié)點是可靠的。在后者情況中,,一個用戶空間的有用的程序?qū)玫?,用來分析所有介質(zhì)上的節(jié)點和修復(fù)或重建損壞或丟失的nodes。有了兩個主節(jié)點的拷貝,,就可以知道發(fā)生了哪種情況,,根據(jù)情況去回復(fù)。 LEBs zero存儲著超級塊的節(jié)點,。超級塊包含著極少改變的文件系統(tǒng)的參數(shù),。比如說,flash的幾何尺寸(eraseblock大小,、數(shù)目等)存儲在超級塊中,。到目前為止,只有一種超級塊重寫的情況,,那就是當(dāng)自動改變尺寸發(fā)生,。UBIFS目前只有一個非常有限的能力去改變尺寸,只能當(dāng)文件系統(tǒng)創(chuàng)建時改變到最大的尺寸,。這個特性是需要的,,因為flash分區(qū)的實際大小是不同的,因為有變化數(shù)量的壞塊存在,。所以當(dāng)文件系統(tǒng)鏡像通過mkfs.ubifs創(chuàng)建時,,最大數(shù)量的eraseblock被指定,,鏡像在超級塊中記錄這個數(shù)值和被用的實際eraseblocks數(shù)量。當(dāng)UBIFS被掛載到一個分區(qū)(實際上是一個UBI卷),,如果卷中 的eraseblock數(shù)量比記錄在超級塊中的大而比最大值(也記錄在超級塊中)小,,那么ubifs自動改變大小以適應(yīng)這個卷。 實際上在UBIFS中有6個areas,,它們的位置當(dāng)文件系統(tǒng)創(chuàng)建時候確定,。前兩個areas已經(jīng)描述過了。LEB 0是超級塊區(qū),。超級塊通常是偏移0,,超級塊被寫入使用UBI的原子LEB改變的能力。下一個區(qū)域是主節(jié)點區(qū),,其占有了LEB 1和LEB 2,。一般來說,這兩個LEB包含著指定的data,。主節(jié)點被寫到每個LEB中連續(xù)不斷的位置直到?jīng)]有空間為止,,在這個點,LEBs被unmapped,,主節(jié)點被寫入到0偏移的位置(自動使UBIFS map一個擦除過的LEB),。注意,主節(jié)點LEBs不是同時被unmapped,,因為那將會導(dǎo)致文件系統(tǒng)臨時沒有有效的主節(jié)點,。其它的UBIFS區(qū)域是the log area, the LEB properties tree(LPT)area, the orphan area 和 the main area。 log是UBIFS日志的一部分,。UBIFS使用日志的目的是為了減少對falsh index的更新頻率,。回憶一下,,index組成了wandering tree(只由index node組成)的頂部分,,更新文件系統(tǒng)時,一個leaf node必須被添加或者替代到wandering tree,,還有所有的祖先索引節(jié)點根據(jù)情況地更新,。這個將會非常沒有效率的如果index被更新當(dāng)每次leaf node被寫入,因為多數(shù)相同的索引節(jié)點被反復(fù)地寫入,,尤其tree的頭部,。作為代替,UBIFS定義了一個日志,,樹葉節(jié)點被寫到這個日志里但是不立即添加到flash index中,。注意在memory中的index被更新。定期地,當(dāng)日志被認(rèn)為差不多full,,它將會被提交,。提交過程由寫新版本index和相符合的主節(jié)點。 日志的存在意味著當(dāng)UBIFS被掛載時,,on-flash index已經(jīng)過時,。為了將它更新,日志中的頁節(jié)點必須被讀和重新索引,。這個過程叫replay,。注意,日志越大,,replay花的時間越長,,掛載UBIFS文件系統(tǒng)的時間也會越長。另一方面,,一個大的日志很少被提交,,這會是文件系統(tǒng)很有效率。日志的大小是mkfs.ubifs的一個參數(shù),,所以它可以被選擇,,以至于來滿足文件系統(tǒng)的需要。無論怎么樣,,UBIFS默認(rèn)不使用fast unmount選項,,取而代之的是unmount前會運行一次提交。這樣當(dāng)文件系統(tǒng)再次被掛載時,,日志幾乎是空的,,這確實使掛載非常快速,。這是一個很好的權(quán)衡協(xié)調(diào),因為提交過程本身一般是非??斓?,只花費一點點時間。 注意提交過程不會從日志中移走leaf nodes,,而是日志移走,。記錄日志的地方是log的目的。log包含兩種nodes,。一個是提交開始節(jié)點(commit start node),,記錄著一個提交已經(jīng)開始。另一個節(jié)點是相關(guān)節(jié)點(reference node),,記錄著組成余下的日志的主區(qū)域(main area)的LEB的數(shù)量,。這些LEBs叫做芽(buds),所以日志由log和buds組成。log大小是有限的,,可以認(rèn)為是一個環(huán)形bufer,。提交過后,記錄著日志的先前位置的相關(guān)節(jié)點已經(jīng)不再需要了,,所以log的尾部被擦除,,同時log的頭部被延長。相對于commit-start node記錄提交的開始,,master node的寫入表示提交的結(jié)束,,因為主節(jié)點指向新的log的尾部。如果因為文件系統(tǒng)被不干凈地卸載導(dǎo)致提交沒有完成,,然后replay process replays both the old and new journal,。 由于幾種情況使replay process變得復(fù)雜。第一種情況是leaf nodes 必須按順序replay,。因為UBIFS使用一個多頭日志(multiheaded journal),,寫leaf nodes順序不是簡單的跟log中涉及到的bud eraseblocks的順序一致。為了有序地排列leaf nodes,,每個nodes包含了一個64-bit 序列號,,該號在文件系統(tǒng)的活動期內(nèi)會增加。Replay把日志中的所有leaf nodes都讀出來,,然后把他們放到一個RB-tree中,,這個RB-tree是按照序列號存儲的。然后按順序的壓縮RB-tree,,根據(jù)實際情況更新內(nèi)存中的index,。 第二個復(fù)雜情況就是replay必須管理刪除和截斷。有兩種刪除,。節(jié)點刪除跟文件和目錄刪除是一致的,,目錄項刪除跟解開連接和重命名是一致的。在UBIFS中,,inodes有一個一致的inode node,,inode node記錄了目錄項連接號,更多地簡單認(rèn)為是連接數(shù)目,。當(dāng)一個inode被刪除,,一個連接數(shù)目為0的inode node被寫入到日志中。在這種復(fù)雜情況下,,不是將那個leaf nodes添加到index中,,而是根據(jù)inode號沿著所有index entries,將它移除,。 如果刪除目錄項,,一個目錄項的節(jié)點被寫到日志中,但是先前目錄項涉及到的inode號被設(shè)為0。注意目錄項中有inode號,。一個是其父目錄項的號,,一個是其文件或子目錄項的號。刪除目錄項是后者被設(shè)置為0,。當(dāng)replay處理一個inode號為0的目錄項時,,它將把那個目錄項從index中移除。 截斷,,當(dāng)然是改變文件的大小,。事實上,截斷既可以延長文件的長度又可以縮短文件的長度,。對于UBIFS,,延長文件的長度不需要特殊的控制。用文件系統(tǒng)的說法,,延長文件的長度通過截斷建立一個hole去延長,,這個hole是不能被寫入的,文件的這部分被假設(shè)為0,。UBIFS不索引holes,,也不存儲任何對應(yīng)于holes的nodes。代替一個hole是index entry,。當(dāng)UBIFS尋找index,,發(fā)現(xiàn)沒有index entry,那么它將定義為hole,,并創(chuàng)建0數(shù)據(jù),。另外一方面,縮短文件長度的截斷要求新文件長度以外的data nodes要從index中移除,。為了這種情況發(fā)生,,截斷節(jié)點被寫到日志中,截斷節(jié)點記錄著老的和新的文件長度,。Replay通過一致的index entry處理這些節(jié)點,。 第三個復(fù)雜情況是repaly必須使LEB properties tree(LPT)區(qū)更新。LEB properties是三個對于main area的所有LEB要知道的值,。這些值分別是:剩余空間,臟空間和該eraseblock是否是index eraseblock,。注意index nodes和 non-index nodes永遠不在同一塊eraseblock中,,因此一個index eraseblock只包含index nodes,一個non-index eraseblock也只包含non-index nodes.剩余空間是指到eraseblock的結(jié)尾還沒被寫,,還可以填充更多的nodes的字節(jié)數(shù),。臟空間是指過時節(jié)點和填料(潛在可以被垃圾回收的)的字節(jié)數(shù)。對于發(fā)現(xiàn)空間加到日志或者索引,對于發(fā)現(xiàn)最臟的eraseblock去垃圾回收,,LEB properties是必要的,。每當(dāng)一個節(jié)點被寫入,那個eraseblock剩余空間就會被減少,。每當(dāng)一個節(jié)點過時或者填料節(jié)點被寫入或者一個截斷(或刪除)節(jié)點被寫入,,那個eraseblock的臟空間會增加。當(dāng)一個eraseblock被申請為index,,那必須要記錄一下,。例如,一個有剩余空間的index eraseblock沒有被申請到日志,,那么它將會導(dǎo)致index和non-index nodes混合于一個eraseblock,。后面預(yù)算章節(jié)將會進一步講述index和non-index nodes不能混合的理由。 一般來說,,index子系統(tǒng)自己負(fù)責(zé)通知LEB properties子系統(tǒng)LEB properties改變,。在replay中LEB properties增加的復(fù)雜度發(fā)生在當(dāng)一個垃圾回收的eraseblock添加到日志中。像索引一樣,,LPT區(qū)域只有提交時才能被更新,。和所以一樣,on-flash LPT在掛載時已經(jīng)過時,,必須通過replay process更新,。所以on-flash LEB properties只反映出最后一次提交的狀態(tài)。Replay將開始更新LEB properties,無論有的改變發(fā)生在垃圾回收之前還是在垃圾回收之后,。根據(jù)垃圾回收點的不同,,最終的LEB property的值將會是不同的。為了控制這個,,replay插入一個涉及到它的RB-tree去描繪LEB添加到日志時候的點,。這使replay能正確地適應(yīng)LEB property值當(dāng)RB-tree被應(yīng)用到index中。 第四個復(fù)雜情況是恢復(fù)的效果,。UBIFS記錄著主節(jié)點無論文件系統(tǒng)是否被干凈地卸載,。如果不是干凈的,某個錯誤條件翻轉(zhuǎn)recovery,,使之適應(yīng)文件系統(tǒng),。replay被兩種情況影響。第一,,一個bud eraseblock可能損壞,,因為它正在寫的時候被不干凈地卸載了。第二,,同樣理由,,log eraseblock也可能被損壞,。replay通過這個eraseblock到recovery試圖適應(yīng)這些nodes在這些eraseblocks中來處理這些情況。如果文件系統(tǒng)被掛載成可讀寫,,那么recovery將做一些必要的fixes,。在這中情況下,完整的被恢復(fù)的UBIFS文件系統(tǒng)和沒有遭遇過不干凈卸載一樣的完美,。如果文件系統(tǒng)被掛載成只讀,,恢復(fù)將會被延期直到文件系統(tǒng)被掛載成可讀寫。 最后一個復(fù)雜情況是有些相關(guān)的leaf nodes可能已經(jīng)不存在了,。這個發(fā)生在當(dāng)nodes已經(jīng)被刪除,,包含它的eraseblock隨后已經(jīng)被垃圾回收了。一般來說,,已刪除的leaf nodes不會影響replay,因為它們不是index的一部分,。不管怎么樣,index結(jié)構(gòu)一方面要求leaf nodes有時候被讀以更新index,。這個發(fā)生是因為目錄項nodes(擴展的屬性項nodes),。在UBIFS中,一個目錄由一個inode node和一個目錄項結(jié)構(gòu)組成,。通向index是一個node key,,它是一個確認(rèn)node的64-bit的值。在大多數(shù)情況下,,這個node key唯一確認(rèn)這個node,,所以index被更新用的就是這個key。不幸的是,,目錄項的指定信息是名字,,它是一個很長的字符(在ubifs中達到255個字符)。為了將該信息擠到64-bit中,,它的名字被hash到一個29-bit的值中,,這個不是唯一地對于名字。當(dāng)兩個名字給出來相同的hash值,,這叫哈希相撞(hash collision),。在這種情況下,leaf nodes必須被讀出來,,通過比較存儲在leaf nodes中的名字來解決相撞,。所以如果因為上述原因,leaf nodes將會發(fā)生什么,,這個不會太糟糕,。目錄項節(jié)點曾經(jīng)被添加和移除,他們永遠也不會被代替,,因為他們包含的信息永遠不改變,。當(dāng)增加一個hash key節(jié)點,將不會有匹配,。當(dāng)移除一個hashed-key節(jié)點,,通常會有一個匹配無論是已經(jīng)存在的node或者對一個有正確key丟掉的node。為了提供這個特殊的索引更新replay,,一個獨立設(shè)置的功能被使用,。 Log區(qū)后面是LPT area。log區(qū)的大小當(dāng)文件系統(tǒng)被創(chuàng)建的時候定義創(chuàng)建的,,也就是順序地就是LPT區(qū)的開始,。目前,LPT區(qū)的大小是基于LEB大小和當(dāng)文件系統(tǒng)創(chuàng)建時指定的最大的LEB數(shù)目自動計算的,。和log區(qū)一樣,,LPT區(qū)必須永不超出空間。不像log區(qū)是,,LPT區(qū)更新不是自然順序的,,他們是隨機的。另外,,LEB properties 數(shù)據(jù)的數(shù)量潛在地非常巨大的,,通過它必須是可量的。解決方法是存儲LEB properties 到一個wandering tree,。實際上LPT區(qū)非常像一個微型的文件系統(tǒng),。它有自己的LEB properties,那就是LEB properties 區(qū)的LEB properties(稱著ltab),。它有自己的垃圾回收,。它有自己的節(jié)點結(jié)構(gòu)。無論如何,,和index一樣,,LPT區(qū)只在提交時更新。因此on-flash index和on-flash LPT描繪最后一次提交文件系統(tǒng)是什么樣的,。和文件系統(tǒng)的實際狀態(tài)不同點被日志中的節(jié)點描述,。 LPT實際上有兩個稍微不同的形式,稱為小模式和大模式,。當(dāng)整個LEB properties table 可以寫到一個eraseblock,,小模式被使用。在那種情況下,,LPT垃圾回收就是寫整個表,,這因此導(dǎo)致所以其他LPT區(qū)eraseblocks 可重復(fù)使用。在大模式下,,臟LPT eraseblocks被選擇為垃圾回收,,由記錄LEB的節(jié)點臟和寫臟節(jié)點組成,。當(dāng)然,在大模式下,,LEB號的表被存儲以至于當(dāng)UBIFS第一次掛載時,,尋找空eraseblock不會搜尋整個LPT。在小模式,,它是假設(shè)搜尋整個表不是很慢的,,因為它很小。 UBIFS的一個主要任務(wù)是通向index,,它是一個wandering tree,。為了使其更有效,index nodes被緩存在內(nèi)存中一個叫tree node cache(TNC)的結(jié)構(gòu)里,。TNC是一個B+tree,,和on-flash index相同的node for node,添加自從上次以來的所有改動。TNC的nodes稱為znodes,。另外一種看法是一個znode,,當(dāng)on-flash被稱為一個index node,一個index node在內(nèi)存中稱為一個znode,。最初是沒有znode的,。當(dāng)在index上搜尋時,index nodes需要讀出來,,將他們當(dāng)作znodes添加到TNC,。當(dāng)一個znode需要改變,就將其標(biāo)記為臟放在內(nèi)存中直到下一次提交它又再一次變?yōu)楦蓛?。在任何時候,,UBIFS內(nèi)存shrinker可能決定釋放TNC中的干凈的znodes,以至于大量需要的內(nèi)存和部分使用的index大小相稱,,注意是全部大小,。另外,放開TNC的底部是一個leaf node cache(LNC),它是只用來為目錄項的,。LNC需要緩存被讀的節(jié)點是碰撞解決或是目錄讀操作的結(jié)果,。因為LNC依附于TNC,這個有效地得到收縮(shrink)當(dāng)TNC做此操作時,。 進一步描述TNC復(fù)雜性使提交和其它UBIFS操作產(chǎn)生盡可能少的沖突,。為了做這個,提交被分成兩個主要部分,。第一個部分叫提交開始(commit start),。在提交開始期間,提交寫信號量down,,這防止期間對日志的更新,。在這期間,,TNC子系統(tǒng)產(chǎn)生很多臟的znodes和找到他們將被寫入flash的位置。然后提交釋放信號量,,一個新的日志開始被使用,,同時提交的過程在繼續(xù)。第二部分叫提交結(jié)束(commit end),。在提交結(jié)束期間,TNC寫新的index nodes,,但是不帶任何鎖(即類似前面的信號量)的,。也就是TNC可以被更新同時新的index可以被寫到flash中。這是通過標(biāo)記znodes完成的,,稱為copy-on-write,。如果一個znode提交時需要被改變,那么將拷貝一份,,以至于提交看到的仍然是沒改變的znode,。另外,提交是UBIFS的后臺線程運行的,,這樣用戶進程對于提交的只需等待盡可能少的時間,。 接下來LPT和TNC采用了相同的提交策略,他們都是B+tree組成的wandering trees,。這導(dǎo)致了代碼方面很多的相似性,。 UBIFS和JFFS2之間有三個重要的不同點。第一個已經(jīng)提到過了:UBIFS有on-flash index而JFFS2沒有,。第二個不同點是暗含的:UBIFS運行在UBI層(運行在MTD層)上的,,而JFFS2直接運行在MTD層。UBIFS得益于UBI的損益平衡和錯誤管理,,這些占用的flash空間,、內(nèi)存和其它資源都是有UBI分配。第三個重要的不同點是UBIFS允許回寫(writeback). Writeback是VFS的一個特征,,它允許寫data到緩存中不立即寫到介質(zhì)中,。這是系統(tǒng)更好潛在地更有效,因為對同一個文件的更新可以一塊分組,。支持writeback的困難是這個要求文件系統(tǒng)知道有多少剩余空間是有效的以至于緩存永遠不要大于介質(zhì)的空間,。對于UBIFS,這點是非常困難決定的,,所以一個整個稱為預(yù)算(budgeting)的子系統(tǒng)專門做這個事情,。困難有好幾個理由。 第一個理由就是UBIFS支持透明的壓縮,。因為壓縮的數(shù)量是不知道的,,需要的空間的數(shù)量是不知道的,。預(yù)算必須假設(shè)最糟的情況,假設(shè)沒有壓縮,。無論怎么樣,,多數(shù)情況下是一個不好的假設(shè)。為了克服這個,,當(dāng)察覺到空間不足時預(yù)算開始強制回寫,。 第二個理由是垃圾回收不保證能收回所有的臟空間。UBIFS垃圾回收一次處理一個eraseblock,。如果是NAND flash,,只有完整的NAND頁可以寫。一個NAND eraseblock由固定數(shù)量的nand pages組成,。UBIFS稱nand page大小為最小的I/O單元,。因為UBIFS一次處理一個eraseblock,如果臟空間少于最小的I/O大小,,它是不能被回收的,,它將以填料結(jié)束。當(dāng)一個eraseblock的臟空間少于最小I/O大小,,那個空間稱為死區(qū)(dead space),。死區(qū)是不回收的。 類似于死區(qū),,還有一個叫暗區(qū)(dark space),。暗區(qū)是一個eraseblock的臟空間少于最小node大小。最壞的情況,,文件系統(tǒng)滿是最大大小的nodes,,垃圾回收將沒有結(jié)果在多片剩余空間。所以在最壞的情況下,,暗區(qū)是不回收的,。在最好的情況下,它是可以回收的,。UBIFS預(yù)算必須假設(shè)最壞的情況,,所以死區(qū)和暗區(qū)都被假設(shè)為無效的。無論如何,,如果有不充足的空間,,但是有很多暗區(qū),預(yù)算自身運行垃圾回收看是否能釋放更的空間,。 第三個理由是緩沖數(shù)據(jù)可能是存儲在flash 上的過時數(shù)據(jù),。是否是這種情況通常是不知道的,壓縮中有什么不同點一般也是不知道的。預(yù)算強制回寫當(dāng)它計算不充足空間時,。只有試著回寫,、垃圾回收和提交日志后,預(yù)算將放棄并返回ENOSPC(沒有空間錯誤碼),。 當(dāng)然,,那就意味著當(dāng)文件系統(tǒng)接近滿時,UBIFS將變得很無效,。實際上,,所有falsh文件系統(tǒng)都是這樣。 第四個理由是刪除和截斷需要寫新節(jié)點,。因此如果文件系統(tǒng)真的沒空間了,,它將不可能刪除任何東西的因為已經(jīng)沒有空間來寫刪除節(jié)點的節(jié)點或者截斷節(jié)點了。為了防止這種情況,,UBIFS經(jīng)常保留一些空間,允許刪除和截斷,。 下一個UBIFS區(qū)是孤立區(qū)(orphan area),。一個orphan是一個節(jié)點號,該節(jié)點號的節(jié)點的節(jié)點已經(jīng)被提交到index,,但link數(shù)為0,。這個發(fā)生在當(dāng)一個打開的文件被刪除,然后允許了提交,。正常情況下,,該inode應(yīng)該被刪除當(dāng)文件被關(guān)閉的時候。無論如何,,在不干凈卸載的情況下,,orphans需要被記錄。不干凈卸載后,,無論是搜尋整個index還是保持一個list在flash的某處,,orphans' inodes必須被刪除。 孤立區(qū)是一個固定數(shù)量的LEBs,,位于LPT area和main area,。孤立區(qū)LEBs的數(shù)量當(dāng)文件系統(tǒng)創(chuàng)建時指定。最小數(shù)量是1,。孤立區(qū)的大小可以適應(yīng)在一個LEB中:(leb_size-32)/8 例如,,一個15872字節(jié)的LEB可以適應(yīng)1980個orphans所以一個LEB已經(jīng)足夠了。 orphans被累積在一個RB-tree,。當(dāng)節(jié)點的link數(shù)變?yōu)?font face="AR PL UMing CN, serif">0,,這個inode號被添加到這個RB-tree。當(dāng)inode被刪除,它將從tree中移除,。當(dāng)提交運行時,,任何orphans樹中新orphans被寫到orphan區(qū)。如果orphan區(qū)已經(jīng)滿,,空間將被擴大,。通常會有足夠大的空間的因為通常會防止用戶創(chuàng)建多于允許的最大數(shù)目的orphans。 最后一個UBIFS區(qū)是主存儲區(qū)(main area),。Main area包含由文件系統(tǒng)數(shù)據(jù)和index組成的節(jié)點,。一個main area LEB可能是一個index eraseblock或者是一個non-index eraseblock。一個non-index eraseblock可能是一個芽或者已經(jīng)被提交,。一個芽可能是當(dāng)前日志頭中的一個,。一個包含提交節(jié)點LEB仍然可以變成一個芽如果它還有剩余空間。因此一個芽LEB從日志開始的地方有一個偏移,,盡管偏移通常為0,。 |
|