編者註:
最近剛好接觸到UBIFS,於是就在UBIFS官網下載的ubifs_whitepaper(ubifs白皮書)學習,後來發現ChinaUnix部落格上有這篇文檔的翻譯版本,但是經過我一番研讀後發現那個翻譯版本有如下幾個小缺點:
1、中英文混雜,文章中有大量的英文短語短句還未翻譯
2、毫無頭緒 ,這個不能怪那篇博主,因為這個白皮書就是毫無章節的概念,所以我的譯文中我自己添加了章節以及標記,方便大家閱讀
3、原文中有些難以翻譯的可能翻譯有誤,對於這種“長難句”,我做了些工作,盡量保證正確以及方便大家理解!
好了,翻譯是個很枯燥很無聊的工作,所以在這對那個翻譯的博主表示感謝,因為本文就是在那個翻譯的基礎上改進的!
====================================================華麗的本文分割線====================================================
一、Flash檔案系統設計思路
為flash 設計的檔案系統要求異地更新(out-of-place update)。因為flash 在寫之前必須要先擦除,且再次擦除之前只能寫一次。如果擦除塊(eraseblocks)很小且可以快速擦除,那麼可以將它們看作磁碟扇區(disk
sector),但是實際上並不是這樣。讀出一個整塊的擦除塊,擦除它,再回寫更新的資料,所花時間比單獨在其它已經擦除了的擦除塊更新資料長100倍。換句話說,對於一個小的更新,在本地更新比異地更新更新時間長100倍。
異地更新要求記憶體回收(Garbage Collection)。當有資料異地更新時,原來的擦除塊就可能同時包含有效資料和廢棄資料(這些資料已經在別的地方更新)。這樣到最後,檔案系統將用完所有空的擦除塊,而且每個擦除塊包括有效資料和廢棄資料。為了在別的地方寫新的資料,必須有一個擦除塊是空的可用於擦除和重新使用。尋找包含許多廢棄資料的擦除塊,並移動有效資料至其它擦除塊,這個過程叫記憶體回收。
記憶體回收讓我們想起節點結構的好處。為了能回收一塊擦除塊,檔案系統必須能確認儲存的資料。這是檔案系統面對的一般索引問題的矛盾。檔案系統通常以一個檔案名稱開始和必須找到屬於這個檔案的資料。記憶體回收可以開始於任何資料且必鬚髮現這些資料屬於哪個檔案。解決這個問題的一個方法是根據檔案的資料存放區中繼資料(metadata)。資料和中繼資料結合在一起稱為一個節點(node)。每一個節點記錄著其屬於哪個檔案和這個節點包含著什麼資料。JFFS2和UBIFS都是按照一個節點結構設計的,節點結構使能它們的記憶體回收行程直接讀擦除塊,決定哪些資料需要移動,哪些要丟棄,根據實際情況去改變索引。
二、UBIFS設計簡介
JFFS2和UBIFS最大的不同就是UBIFS將索引儲存在flash上,而JFFS2的索引儲存在記憶體中,當檔案系統被掛載的時候,記憶體需要重建立立索引。JFFS2這樣就潛在地給自己最大尺寸做了一個限制,因為掛載時間和記憶體使用量情況都隨著flash的大小而線性增長,BUIFS就是被設計用來克服這個限制。
不幸的是,將索引儲存在flash上是非常複雜的,因為索引本身也需要異地更新。當索引的一部分被異地更新,那麼與更新的索引相關的其它部分索引也必須要更新。然後,依次地,相關部分的相關部分也必須被更新。這樣看起來好像永無止境地更新下去,一個解決的辦法就是使用游離樹(wandering tree)。
對於UBIFS,游離樹(wandering tree,實際上是一個B+樹)只有樹上的葉子包含檔案資訊。他們是檔案系統的有效節點。樹的內部元素是索引節點(index nodes)並包含子節點的相關資訊。也就是說,一個節點記錄著它的子節點的位置。所以UBIFS 的游離樹可以看成兩個部分。頂部分由建立樹結構的索引節點群組成,底部分由指向實際資料的葉子節點組成。頂部分可以簡單地看作索引index。一個檔案系統的更新過程由建立一個新的葉子節點和添加該節點到樹上(或者代替原來的樹中的節點)組成。之後,其父索引節點必須也要被代替,從而父節點的父節點,一直到樹的根(root),都必須要被代替。要被代替的索引節點數等於樹的高度。這裡留下的問題就是怎麼知道樹的根在哪裡。在UBIFS中,根索引節點的位置儲存在主節點(master
node)裡。
2.1 主節點區
主節點儲存著根索引節點的位置。主節點自身被重複寫到LEB1和LEB2(LEB,logical eraseblocks邏輯擦除塊)。LEBs是UBI建立的一種抽象。UBI將PEBs(physical
eraseblocks,物理擦除塊)映射到LEBs,所以LEB1和LEB2可能是flash介質上的任何地方(嚴格來說是UBI裝置),無論如何,UBI都記錄著他們在哪裡。兩個擦除塊被使用,是為了保證有兩個主節點的拷貝。這是為了恢複著想,因為有兩種情況會導致主節點損壞或丟失:第一種情況就是當主節點正在被寫入的時候突然斷電;第二種情況是可能是flash介質自身損壞。對於第一種情況是可以的恢複,因為先前的主節點還可以使用。對於第二種情況是不能的恢複,因為無法確定哪一個主節點是可靠的。在後者情況中,將會用到一個使用者空間的程式,用來分析所有介質上的節點並修複或重建損壞或丟失的節點。有了兩個主節點的拷貝,就可以知道發生了哪種情況,根據情況去恢複。
2.2 超級塊區
LEB0儲存著超級塊的節點。超級塊包含著一些檔案系統的極少改變的參數。比如說,flash的幾何尺寸(擦除塊大小、數目等)就儲存在超級塊中。到目前為止,只有一種情況需要重寫超級塊,那就是當發生自動改變尺寸。UBIFS目前只有一個非常有限的能力去改變尺寸,只能在檔案系統建立時改變到最大的尺寸。這個特性是需要的,因為有不定數量的壞塊導致flash分區的實際大小不同。所以當通過mkfs.ubifs建立檔案系統鏡像時,最大數量的擦除塊需要被指定,鏡像在超級塊中記錄這個最大數以及被實際使用的擦除塊數量。當UBIFS被掛載到一個分區(實際上是一個UBI卷),如果卷中的擦除塊數量比記錄在超級塊中的擦除塊數大並比最大擦除塊數(也記錄在超級塊中)小,那麼ubifs自動改變大小以適應這個卷。
實際上在UBIFS中有6個地區,它們的位置在檔案系統建立時候確定。前兩個地區已經描述過了。LEB0是超級塊區,超級塊通常是位移0,寫超級塊區時通常用的是LEB的原子修改功能,從而保證LEB要麼成功修改要麼未成功。下一個地區是主節點區,其佔有了LEB1、LEB2。一般來說,這兩個LEB包含著相同的資料。主節點通常被連續寫到LEB直到該LEB寫滿,這時候,該LEBs被取消映射(unmap),主節點被寫入到0位移的位置(UBIFS
重新對應主節點地區LEB到一個擦除過的PEB)。注意,主節點LEBs不是同時被取消映射,因為那將會導致檔案系統臨時沒有有效主節點。其它的UBIFS地區是log日誌區(the log area),LPT區(the LEB properties tree area), 孤兒區(the orphan area)和主儲存區(the main area)。
2.3 log日誌區
log是UBIFS日誌的一部分。UBIFS使用日誌的目的是為了減少對falsh索引的更新頻率。回憶一下,索引組成了游離樹的頂部分(只由索引節點群組成),更新檔案系統時,添加或者替代游離樹中的一個葉子節點時,該葉子節點的所有祖先索引節點都需要根據情況更新。如果當每次葉子節點被寫入索引都需要更新將會導致效率非常低下,因為多數相同的索引節點被反覆地寫入,尤其樹的頭部。所以,UBIFS定義了一個日誌,葉子節點被寫到這個日誌裡而不是立即添加到flash索引中。注意此時在記憶體中的索引需要更新(見TNC)。定期地,日誌差不多滿了,它將會被提交。提交過程包括寫新的索引和主節點。
日誌的存在意味著當UBIFS被掛載時,存在flash上的索引已經過時。為了更新它,必須讀日誌中的葉子節點並重新索引。這個過程叫回放(replay)。注意,日誌越大,回放花的時間越長,掛載UBIFS檔案系統的時間也會越長。另一方面,一個大的日誌很少被提交,這會使檔案系統很有效率。日誌的大小是mkfs.ubifs的一個參數,所以它可以被修改,從而滿足檔案系統的需要。無論怎麼樣,UBIFS預設不使用快速卸載(fast unmount)選項,取而代之的是卸載前會運行一次提交。這樣,當檔案系統再次被掛載時,日誌幾乎是空的,使掛載非常快速。這是一個很好的權衡協調,因為提交過程本身一般是非常快的,只花費一點點時間。
注意提交過程不是從日誌中移走葉子節點,而是移動日誌。log的目的就是記錄日誌的位置。log包含兩種節點:一個是提交開始節點(commit start node),記錄著一個提交已經開始。另一個節點是相關節點(reference node),記錄著組成餘下的日誌的主儲存區(main area)的LEB數量。這些LEBs叫做芽(buds),所以日誌由log和芽組成。log的大小是有限的,可以認為是一個環形緩衝區。提交過後,記錄著先前日誌位置的相關節點已經不再需要了,所以log的尾部被擦除,同時log的頭部被延長。相對於提交開始節點記錄提交的開始,主節點的寫入表示提交的結束,因為主節點指向新的log的尾部。如果因為檔案系統被不乾淨地卸載導致提交沒有完成,然後回放操作會回放老的和新的日誌(從而使得日誌一致)。
由於幾種情況使回放操作變得複雜:
>> 第一種情況是葉子節點必須按順序回放。因為UBIFS使用一種多頭日誌(multiheaded journal),寫入葉子節點的順序不是簡單的跟log中涉及到的芽擦除塊的順序一致。為了給葉子節點排序,每個節點包含了一個64bit的序號,該號在檔案系統活動時會增加。回放把日誌中的所有葉子節點都讀出來,然後把他們放到一個紅/黑樹狀結構中,這個紅/黑樹狀結構是按照序號儲存的。之後會按順序地處理紅/黑樹狀結構,並實際情況更新記憶體中的索引。
>> 第二個複雜情況就是回放必須管理刪除和截斷。有兩種刪除。Inode節點刪除相當於刪除檔案和目錄,以及目錄項刪除即刪除串連和重新命名。在UBIFS中,inodes有一個一致的inode節點,inode節點記錄了目錄項串連號,更多地簡單認為是串連數目。當一個inode被刪除,一個串連數目為0的inode節點被寫入到日誌中。在這種複雜情況下,不是將那個葉子節點添加到索引中,而是根據inode號沿著所有索引項目,將它移除。如果刪除目錄項,一個目錄項的節點被寫到日誌中,但是先前目錄項涉及到的inode號被設為0。注意目錄項中有兩個inode號。一個是其父目錄項的號,一個是其檔案或子目錄項的號。刪除目錄項是後者被設定為0。當回放處理一個inode號為0的目錄項時,它會直接將那個目錄項從索引中移除而不是添加。
截斷即是改變檔案的大小。事實上,截斷既可以延長檔案的長度又可以縮短檔案的長度。對於UBIFS,延長檔案的長度不需要特殊的控制。用檔案系統的說法,通過截斷延長檔案的長度會建立一個hole,這個hole在檔案中是不能被寫入的,而且是全0位。UBIFS不索引holes,也不儲存任何對應於holes的節點。代替一個hole是不在那的索引項目。當UBIFS尋找index,發現沒有索引項目,那麼它將定義為hole,並建立0資料。另外一方面,縮短檔案長度的截斷需要將多餘的節點從索引中移除。為了這種情況發生,截斷節點被寫到日誌中,截斷節點記錄著老的和新的檔案長度。回放通過刪除相關的索引項目處理這些節點。
>> 第三個複雜情況是回放必須更新LPT區(LEB properties tree 邏輯擦除塊屬性樹)。LEB 屬性是在主儲存區中對於所有LEB都要知道三個值。這些值分別是:空閑空間,髒空間以及該擦除塊是否是索引擦除塊。注意索引節點和非索引節點永遠不在同一塊擦除塊中,因此一個索引擦除塊是一個只包含索引節點的擦除塊,一個非索引擦除塊也只包含非索引節點。空閑空間是指該擦除塊的結尾還沒被寫還可以填充更多的節點的地區的位元組數。髒空間是指廢棄節點和填充的位元組數,它們都是潛在可以被記憶體回收的。對於尋找空閑空間用作日誌或者索引,以及尋找最髒的擦除塊做記憶體回收,LEB屬性是必要的。每寫入一個節點,就會減少那個擦除塊的空閑空間。每當廢棄一個節點或者填充節點以及截斷(或刪除)節點時,那個擦除塊的髒空間都需要增加。當一個擦除塊被申請為索引擦除塊,那必須要記錄一下。例如,一個有空閑空間的索引擦除塊就不會被申請用作日誌,因為那樣它將會導致索引和非索引節點混合在一個擦除塊。後面預算章節將會進一步講述索引節點和非索引節點不能混合的理由。
一般來說,索引子系統自己負責將其LEB屬性改變通知LEB屬性子系統。當一個回收過的擦除塊被添加到日誌後在回放時LEB 屬性的複雜度會增加。像索引一樣,LPT地區只在提交時才被更新。和索引一樣,存在flash上的LPT在掛載時已經過時,必須通過回放處理進行更新。所以flash上的 LEB 屬性反映的是最後一次提交時的狀態。回放將開始更新LEB屬性,雖然有的改變發生在記憶體回收之前有的在記憶體回收之後。
根據記憶體回收點的不同,最終的LEB 屬性的值將會是不同的。為了控制這個,回收插入一個引用到它的紅/黑樹狀結構去描繪LEB添加到日誌時候的點(使用log引用節點序號)。當回放紅/黑樹狀結構被應用到索引中時回放能正確地調整LEB 屬性值。
>> 第四個複雜情況是回放時恢複的效果。UBIFS在主節點記錄這檔案系統是否被成功地卸載。如果是不乾淨的卸載(unclean unmount),一定的錯誤條件會觸發檔案系統的恢複。回放被兩種情況影響。第一,一個芽擦除塊正在寫的時候被不乾淨地卸載了,它可能損壞。第二,同樣,log擦除塊可能在寫的時候被不乾淨地卸載導致被損壞。回放會通過恢複這個擦除塊試圖修複其中的節點來處理這些情況。如果檔案系統被掛載成可讀寫,那麼恢複將做一些必要的修複。在這種情況下,被恢複的UBIFS檔案系統的完整性和沒有遭遇過不乾淨卸載一樣的完美。如果檔案系統被掛載成唯讀,恢複將一直等到檔案系統被掛載成可讀寫才做恢複。
>> 最後一個複雜情況是索引中引用的相關的葉子節點可能已經不存在了。這個發生在當節點被刪除而且它所在的擦除塊隨後被記憶體回收處理了。一般來說,已刪除的葉子節點不會影響回放,因為它們不是索引的一部分。但是,索引結構一方面有時候更新索引時會讀葉子節點。在UBIFS中,一個目錄由一個inode節點和一個目錄項組成。可以使用一個節點密鑰(key)獲得索引,密鑰是一個64-bit的值來識別節點。在大多數情況下,這個節點密鑰可以用來唯一確認這個節點,所以索引更新用的就是密鑰。不幸的是,目錄項的指定資訊是名字,它是一個很長的字元(在ubifs中達到255個字元)。為了將該資訊擠到64-bit中,它的名字被hash到一個29-bit的值中,這個對於名字不是唯一地。當兩個名字給出來相同的hash值,這叫雜湊衝突(hash
collision)。在這種情況下,葉子節點必須被讀出來,通過比較儲存在葉子節點中的名字來解決衝突。如果因為上述原因,葉子節點丟失將會發生什嗎?實際上這個不會太糟糕。目錄項節點只會被添加和刪除,它們永遠不會被代替因為他們包含的資訊永遠不改變。當增加一個hash 密鑰節點,將不會有匹配。當移除一個hash密鑰節點,通常會有一個匹配可能是已經存在的節點或者對一個有正確key丟掉的節點。為了提供更新這個特殊的索引用於回放,需要使用一個獨立設定的功能(表示在代碼的首碼“犯錯”)。
2.4 LPT區
Log區後面是LPT區。log區的大小在檔案系統被建立的時候被定義,也就是LPT區的開始在檔案系統建立時也固定了(因為它就跟在log區後)。目前,LPT區的大小是基於在檔案系統建立時指定的LEB大小以及最大的LEB數目自動計算的。和log區一樣,LPT區也不超出空間。不像log區的是,LPT區的更新不是連續的,它們是隨機的。另外,LEB 屬性資料的數量潛在地非常巨大的,而且它必須是可擴充的。
解決方案是儲存LEB 屬性到一個游離樹。實際上LPT區非常像一個微型的檔案系統。它有自己的LEB 屬性,那就是LEB 屬性區的LEB 屬性(稱為ltab)。它還有自己的記憶體回收。它有自己的節點結構--是一個很小的bit層級的。而且,和索引一樣,LPT區只在提交時更新。因此flash上的索引和flash上的LPT描繪的是最後一次提交檔案系統時的狀況。
它和真正的檔案系統的不同點是被日誌中的節點描述。
LPT實際上有兩個稍微不同的形式,稱為小模式(small mode)和大模式(big model)。使用小模式時。整個LEB 屬性工作表可以寫到一個擦除塊。在那種情況下,LPT記憶體回收就是寫整個表,這導致所有其他LPT區擦除塊可重複使用。在大模式下,記憶體回收僅選用髒LPT擦除塊,垃圾回首標記LEB的節點為髒並寫髒節點。當然,在大模式下,會儲存一個LEB數量的表之後UBIFS第一次掛載時,尋找空擦除塊不會搜尋整個LPT。在小模式下,我們假設搜尋整個表不是很慢的,因為它很小。
UBIFS的一個主要任務是讀取索引,索引是一個游離樹。為了使其更有效率,索引節點被緩衝在記憶體中一個叫TNC(tree node cache,樹節點緩衝)的結構裡。TNC是一個B+樹,和flash上的索引相同的節點的節點。TNC的節點稱為znodes。另外一種看法是一個znode在flash上稱為一個索引節點,而一個索引節點在記憶體中稱為一個znode。初始化時是沒有znodes的。當在索引上搜尋時,需要讀索引節點,並將他們當作znodes添加到TNC。當一個znode需要改變,就在記憶體中將其標記為髒直到下一次提交它又再一次標記為乾淨。在任何時候,UBIFS記憶體收縮機制(shrinker)可能決定釋放TNC中的乾淨的znodes,以至於需要的記憶體和在使用的索引大小相稱,注意是索引的全部大小。另外,TNC的底部是一個LNC(leaf
node cache,葉子節點緩衝),它只用來存目錄項的。碰撞解決或是讀目錄操作的節點需要用LNC緩衝。因為LNC依附於TNC,當TNC收縮時LNC也會收縮。
想要使得提交和UBIFS的其他動作產生儘可能少的衝突使得TNC更加複雜。為了達到這個目標,提交被分成兩個主要部分。第一個部分叫提交開始(commit start)。在提交開始期間,提交訊號量down,防止這期間對日誌的更新。在這期間,TNC子系統產生很多髒的znodes並找到他們將被寫入flash的位置。然後釋放提交訊號量,一個新的日誌開始被使用,而此時提交過程仍在繼續。
第二部分叫提交結束(commit end)。在提交結束期間,TNC寫新的索引節點而且是不使用任何鎖(即類似前面的訊號量)。也就是說TNC可以更新並且同時新的index可以被寫到flash中。這是通過標記znodes完成的,稱為寫入時拷貝(copy-on-write)。如果一個znode提交時需要被修改,那麼將拷貝一份,以至於提交看到的仍然是沒改變的znode。另外,提交是UBIFS的後台線程啟動並執行,這樣使用者進程對於提交的只需等待很少的時間。
接下來LPT和TNC採用了相同的提交策略,他們都是使用B+樹實現的游離樹,從而導致了代碼方面很多的相似性。
UBIFS和JFFS2之間有三個重要的不同點。第一個已經提到過了:UBIFS有儲存在flash上的索引而JFFS2沒有(JFFS2的索引在記憶體中),所以UBIFS有可擴充性。第二個不同點是暗含的:UBIFS運行在UBI層,而UBI層運行在MTD層之上,而JFFS2直接運行在MTD層上。UBIFS得益於UBI的損益平衡和錯誤管理,這些佔用的flash空間、記憶體和其它資源都是由UBI分配。第三個重要的不同點是UBIFS允許回寫(writeback).
回寫是VFS的一個特徵,它允許寫data到緩衝中而不是立即寫到介質中。這使系統響應潛在地更有效率,因為對同一個檔案的更新可以組合在一起。回寫的困難之處是要求檔案系統知道有多少空閑空間是有效以至於緩衝不要大於介質的空閑空間。對於UBIFS,這點是非常困難的,所以有個稱為預算(budgeting)的子系統專門做這個工作。困難有好幾個理由:
>> 第一個理由就是UBIFS支援透明的壓縮。因為我們提前不知道壓縮的數量,也不知道的需要的空間數量。預算必須假設最糟的情況---假設沒有壓縮。無論怎麼樣,多數情況下是一個不好的假設。為了克服這個,當察覺到空間不足時預算開始強制回寫。
>> 第二個理由是記憶體回收不能保證回首所有的髒空間。UBIFS記憶體回收一次處理一個擦除塊。如果是NAND flash,一次只能寫一個完整的NAND頁。一個NAND 擦除塊由固定數量的nand頁組成。UBIFS稱nand頁大小為最小的I/O單元。因為UBIFS一次處理一個擦除塊,如果髒空間少於最小的I/O大小,它是不能被回收的,它將作為填充在一個NAND頁的結尾。當一個擦除塊的髒空間少於最小I/O大小,那個空間稱為死區(dead
space)。死區是不可回收的。
類似於死區,還有一種暗區(dark space)。暗區是一個擦除塊的髒空間小於最大節點大小。最壞的情況,檔案系統滿是最大大小的節點,記憶體回收在多片空閑空間將沒有結果。所以在最壞的情況下,暗區是不可回收的。在最好的情況下,它是可以回收的。UBIFS預算必須假設最壞的情況,所以死區和暗區都被假設為無效的。無論如何,如果沒有充足的空間,但是有很多暗區,預算自身會運行記憶體回收看是否能釋放更多的空間。
>> 第三個理由是緩衝的資料可能是儲存在flash上的廢棄資料。是否是這種情況通常是不知道的,壓縮中有什麼不同點一般也是不知道的。這也是當預算計算不充足空間時強制回寫的另一個原因。只有試著回寫、記憶體回收和提交日誌後,預算將放棄並返回ENOSPC(沒有空間錯誤碼)。
當然,那就意味著當檔案系統接近滿時,UBIFS將變得效率很低。實際上,所有falsh檔案系統都是這樣。這是因為有一個空擦除塊在背後已經已擦除是不太可能的,更可能是垃圾收集的運行。
>> 第四個理由是刪除和截斷需要寫新節點。所以如果檔案系統真的沒空間了,它將不可能刪除任何東西,因為已經沒有空間來寫刪除節點的節點或者截斷節點了。為了防止這種情況,UBIFS經常保留一些空間,允許刪除和截斷。
2.5 孤兒區
下一個UBIFS區是孤兒區(orphan area)。一個孤兒是一個節點數,計算的是一些已經被提交到索引的索引節點,它們的連結數為0。這個發生在當一個開啟的檔案被刪除(解除連結),然後執行了提交。正常情況下,該索引應該在檔案被關閉的時候被刪除。然而,在不乾淨的卸載的情況下,孤兒需要被考慮到。不乾淨卸載後,無論是搜尋整個index還是保持一個list在flash的某處,孤兒節點必須被刪除,UBIFS實現的是後者的方案。
孤兒區是有固定數量的LEBs,位於LPT地區和主儲存區之間。孤兒區LEBs的數量當檔案系統建立時指定。最小數量是1。
孤兒區的大小需要可以處理在同一時間預期的最大的孤兒數。孤兒區的大小可以適應在一個LEB中:
(leb_size-32)/8
例如,一個15872位元組的LEB可以適應1980個orphans,所以一個LEB已經足夠了。
孤兒被累積在一個紅/黑樹狀結構中。當inode節點的link數變為0,這個inode號被添加到這個紅/黑樹狀結構。當inode被刪除,它將從tree中移除。當提交運行時,任何孤兒樹中新孤兒被寫到孤兒區,寫到1個或者更多的節點。如果orphan區已滿,空間將被擴大。通常會有總是有足夠的空間,因為驗證可以防止使用者創造超過所允許的最大孤兒數。
2.6 主儲存區
最後一個UBIFS區是主儲存區(main area)。主儲存區包含組成檔案系統的資料和索引節點。一個主儲存區 LEB可能是一個索引擦除塊或者是一個非索引擦除塊。一個非索引擦除塊可能是一個芽或者已經被提交。一個芽可能是當前日誌頭中的一個。一個包含提交過的節點的LEB如果還有空閑空間它仍然可以成為一個芽。因此一個芽LEB從日誌開始的地方有一個位移,儘管位移通常為0。
更多學習參考:
[1] https://zh.wikipedia.org/wiki/UBIFS
[2] http://lwn.net/Articles/290057/
[3] http://lwn.net/Articles/276025/
[4] http://www.linux-mtd.infradead.org/faq/ubifs.html
[5] http://www.linux-mtd.infradead.org/doc/ubifs.html