1. 程式人生 > >UBIFS介紹 raw flash和MMC flash的區別

UBIFS介紹 raw flash和MMC flash的區別

origin: http://sh.516878.com/2013/1101/25699.html

在瞭解UBIFS之前一定要注意UBIFS和任何傳統的檔案系統是不一樣的:UBIFS不是執行在block device之上的(比如hard disk, MMC/SD卡,USB flash驅動等等)。UBIFS是運行於raw flash之上。請在開始UBIFS之旅前確保理解raw flash和MMC flash的區別

  Overview

  UBIFS是nokia工程師在the university of Szeged大學幫助下開發的新的flash file system。UBIFS可以認為是JFFS2檔案系統的下一代產品。

  JFFS2執行在MTD裝置之上,而UBIFS則只能工作於UBI volume之上。也可以說,UBIFS涉及三個子系統:

  1. MTD 子系統, 提供對flash晶片的訪問介面, MTD子系統提供了MTD device的概念,比如/dev/mtdx,MTD可以認為是raw flash

  2. UBI subsystem,為flash device提供了wear-leveling和 volume management功能; UBI工作在MTD裝置之上,提供了UBI volume;UBI是MTD裝置的高層次表示,對上層遮蔽了一些直接使用MTD裝置需要處理的問題,比如wearing-leveling以及壞塊管理。

  3. UBIFS檔案系統,工作於UBI之上

  以下是UBIFS的一些特點:

  可擴充套件性:UBIFS對flash 尺寸有著很好的擴充套件性; 也就是說

mount時間,記憶體消耗以及I/O速度都不依賴於flash 尺寸(對於記憶體消耗的描述並不完全準確,但是依賴性非常的低); UBIFS可以很好的執行在GB級的 flashe裝置; 當然UBI本身還是有擴充套件性的問題,無論如何 UBI/UBIFS都比JFFS2的可擴充套件性好,如果UBI成為瓶頸,可以改進UBI而不需改變UBIFS本身。

  快速mount:不像JFFS2,UBIFS在mount階段不需要掃描整個檔案系統,UBIFS mount的時間只是毫秒級,時間不依賴與flash的尺寸;然而UBI的初始化時間是依賴flash的尺寸的,因此必須把這個時間考慮在內。

  write-back 支援:回寫或者叫

延遲寫更準確些吧,同JFFS2的write-through(立即寫入記憶體)相比可以顯著的提高檔案系統的吞吐量

  異常unmount適應度:UBIFS是一個日誌檔案系統可以容忍突然掉電以及unclean重啟; UBIFS 通過replay 日誌來恢復unclean unmount,在這種情況下replay會消耗一些時間,因此mount時間會稍微增加,但是replay過程並不會掃描整個flash介質,所以 UBIFS的異常mount時間大概在幾分之一秒。

  快速I/O - 即使我們disable write-back(可以在unmount時使用-o sync mount選項), UBIFS的效能仍然接近JFFS2; 記住,JFFS2的同步I/O是非常驚人的,因為JFFS2不需要在flash上維護indexing data結構, 所以就沒有因此而帶來的負擔;而UBIFS恰恰是有index資料的,UBIFS之所以夠快是因為UBIFS提交日誌的方式:不是把資料從一個地方移動到另外一個位置,而只是把資料的地址加到檔案系統的index,然後選擇不同的eraseblock作為新的日誌塊,此外還有multi-headed日誌方式等技巧。

  on-the_flight compression - 儲存在flash介質上的資料是壓縮的;同時也可以靈活的針對單個檔案來開啟關閉壓縮。例如,可能需要針對某個特定的檔案開啟壓縮;或者可能預設方式下支援壓縮,但是對多媒體檔案則關閉壓縮。

  可恢復性 - UBIFS可以從index破壞後恢復; UBIFS中的每一片資訊都用一個header來描述,因此可以通過掃描整個flash介質來重構檔案系統,這點和JFFS2非常類似。想像一下,如果你擦除了FAT檔案系統的FAT表,對於FAT 檔案系統是致命的錯誤,但是如果擦除UBIFS的index,你仍然可以重構檔案系統,當然這需要使用一個使用者空間程式來做恢復

  完整性 - UBIFS通過把checksum寫到flash 介質上來保證資料的完整性,UBIFS不會無視損壞的檔案資料或meta-data;預設的情況,UBIFS僅僅檢查meta-data的CRC,但是你可以通過mount選項,強制進行data CRC的檢查。

  Scalabity

  UBIFS檔案系統的所有資料結構都是使用tree,UBIFS對flash尺寸大小在演算法上是可擴充套件的。然而UBI複雜度隨著flash size線性增長,因此UBI加UBIFS整體上是線性增大的。但是UBIFS的作者認為可以建立一個新的UBI2,克服當前UBI的線性增長。當前的 UBI實現適和2~16GiB大小的raw flashes,具體大小依賴於I/O速度和系統需求。

  注意:儘管UBI是線性增長的,但是它的可擴充套件性依然好過JFFS2,因為JFFS2最初是為32MiB Nor flashes設計的。JFFS2的擴充套件性問題是檔案系統級的,而UBI/UBIFS的擴充套件性問題則是raw flash級的,下表是兩個檔案系統擴充套件性的對比

  Scalability

  issurJffS2UBIFS

  Mount time Linearly depends on the flash sizeTrue. 依賴是線性的,因為JFFS2在mount時要掃描整個flash 介質空間UBIFS mount時間不依賴flash的尺寸。但是UBI需要掃描flash介質儘管掃描時間要比JFFS2快。所以整體上說, UBI/UBIFS是線性依賴的

  記憶體消耗是否線性依賴flash尺寸True. 依賴是線性的UBIFS 記憶體消耗的確依賴flash尺寸,因為LPT shrinker還沒有實現。但是實現LPT shrinker移除依賴性是容易的。當前沒有移除的原因是由於記憶體消耗很小沒必要實現它。 UBI記憶體消耗是線性依賴flash尺寸,因此整體上說UBI/UBIFS線性依賴

  Mount時間是否依賴檔案系統內容True. 儲存在檔案系統內的資料越多,mount時間就越久,因為JFFS2需要做更多的掃邁工作。False. mount時間不依賴檔案系統內容,在最壞的情況下,UBIFS不得不掃描日誌來replay,而日誌是固定大小,並且可配置的

  mount是需要檢查真個檔案系統True. 當JFFS2 mount到NAND flash上時 不得不檢查整個檔案系統。檢查包括讀取每個inode的所有nodes 檢查他們的CRC checksums,這將消耗大量CPU,在mount完JFFS2後,可以通過執行top來觀察CPU佔用率,這也降低了整個系統啟動時間。因為 JFFS2沒有儲存space 管理資訊,因此也需要掃描整個flash介質來獲取這個資訊。False. UBIFS不需要掃描整個檔案系統,因為UBIFS空間管理資訊在LPT(Logical erase block Properties Tree)中

  記憶體消耗是否線性依賴檔案系統尺寸True. JFFS2為flash上的每一個node 儲存了一片資料結構。所以儲存在檔案系統的資料越多,那麼JFFS2所佔用的記憶體越多。False. UBIFS消耗的記憶體不依賴儲存在flash介質上的資料多少

  檔案訪問時間線性依賴檔案的大小Ture. JFFS2在開啟一個檔案時,不得不為檔案節點在記憶體儲存一棵fragment tree. 這棵樹儲存著檔案的偏移所對應的nodes, fragment tree沒有儲存在記憶體中,因此在開啟檔案時不得不讀取這個檔案對應的節點來建立這棵fragment tree。這就意味著,檔案越大,開啟的時間越長。False. UBIFS儲存所有的index B-tree資訊在flash 介質。當要讀取檔案系統的一片資料,在B-tree查詢到對應的flash 地址。當查詢B-tree時會把B-tree節點快取到TNC cache中,cache是可以回收的,這就是說當系統kernel需要更多記憶體時,可以回收cache

  檔案系統性能依賴I/O歷史True. 因為JFFS2是完全同步的,資料一到達立刻就向flash寫入。向一個檔案寫入很少的位元組,JFFS2立即寫入包含了這幾個位元組的node,如果頻繁的隨機寫入,檔案系統就會變得碎片化。JFFS2會合並這些碎塊為4KiB的塊,這涉及到重新壓縮和重寫資料。這個 去碎片化隨機發生在垃圾收集過程,因為JFFS2的磨損平衡演算法是隨機選擇eraseblock。所以如果有很多的小寫入,JFFS2會在某一刻變得非常的慢,系統性能下降使得系統行為變得不可預測False. UBIFS一直寫入4KiB的塊。資料到達後並不是立刻寫入flash,而是推遲以合併後來的寫入,當足夠的資料要求寫入時,才回寫到flash上,回寫通常是background方式

  Write-back support

  UBIFS支援write-back, 意味著檔案的改變並不是立刻提交到flash media上,而是cache這些修改,直到達到寫入的條件。這減少了I/O的數目因此改善I/O效能和系統性能。回寫本身也是檔案系統的標準技術,由於資料沒有立刻寫入flash, 回寫帶來了資料丟失的風險。

  相反, JFFS2不支援write-back, JFFS2檔案系統的所有變化都是立刻同步到flash介質上。事實上,JFFS2有一個很小的buffer大小是NAND page size(如果是 NAND flash)。這個buffer儲存這最後要寫的資料,一旦buffer寫滿立刻就會執行flush。因此JFFS2非常類似於同步檔案系統。

  write-back支援,需要應用程式注意及時同步重要的檔案。否則掉電會導致這些檔案的損壞和消失,掉電對於嵌入式系統而言是很常見的。

  然而一些應用空間程式並沒有考慮write-back這種情況。當這些應用執行在JFFS2上工作良好儘管這些應用是buggy的,一旦執行在 UBIFS上,bugs就很容易重現了。在把JFFS2換作UBIFS後,確保檢查你的應用是否正確處理掉電功能。下列是檢查列表以及一些建議:

  1. 如果你想切換到同步模式,在mount檔案系統是使用 -o 同步選項。然而,要注意檔案系統將會下降。此外,要記住UBIFS mount為同步模式仍然不如JFFS2提供更多的保證

  2. 一定要時刻記住執行fsync在你修改重要資料後;當然,沒有必要sync臨時的檔案;要先考慮檔案資料的重要性,不要執行沒必要的fsync,因為fsync操作會降低效能

  3. 如果你想更精確些,那麼就使用fdatasync,僅僅修改的資料被flushed,而inode meta-data變化不會被flush(比如mtime或者permissions)。

  4. 你也可以在open()呼叫時使用O_SYNC標誌;這使得這個檔案所修改的data(不包括meta-data)都會在write()操作返回前寫入 media;通常來說,最好使用fsync(),因為O_SYNC使得每個寫都是同步的,而fsync允許多個累積的寫。

  5. 還可以使一定數目inodes為同步模式,通過設定inode的sync標誌; 在shell中執行chattr +S;在C程式中,則可以使用FS_IOC_SETFLAGS ioctl命令;注意,mkfs.ubifs工具會檢查原始的FS樹,如果檔案在原始檔案樹是同步的,那麼在UBIFS image也會是同步的。

  要強調的是,上面的方法對於任何檔案系統都是可行的,包括JFFS2。

  fsync()可能包括目錄 - 它同步目錄inode的meta-data。 “sync” flag也可以用在目錄上,使得目錄inode變成同步的。但是"sync" flag是可繼承的,意味這這個目錄下的所有新節點都有這個標誌。 這個目錄的新檔案和新子目錄也就變成同步的, 子目錄的child也是如此。這個功能對於建立一個整個目錄樹都同步的目錄是很有用的。

  fdatasync()呼叫對 UBIFS的目錄是不起作用的,因為UBIFS對目錄項的操作都是同步的,當然不是所有檔案系統都如此。類似的, "dirsync" inode 標誌對UBIFS沒有作用

  以上提到的功能都是作用於檔案描述符,而不是檔案stream(FILE *)。同步一個stream,你應該通過libc的fileno()取得檔案描述符, 首先使用fflush()來flush stream ,然後呼叫fsync或者fdatasync. 你也可以使用其他的同步方法,但是記得在同步檔案前要先flush stream. fflush() 和sync(),fsync,fdatasync與前者的區別,在於前者僅僅同步libc-level的buffer,而後者則是同步kernel- level buffers。

  Write-back knobs in Linux

  Linux有幾個核心引數可以用來調整write-back,可檢視/proc/sys/vm. 這些引數是global, 所以會影響所有的檔案系統。 參考Documentation/sysctl/vm.txt獲取更多的資訊。

  1. dirty_writeback_centisecs: linux週期性write-back執行緒寫出dirty資料的週期,這個機制可以確保所有的髒資料在某個時間點都可以寫入介質。

  2. dirty_expire_centisecs: dirty資料的過期週期,這是資料為dirty的最大時間。過了這個時間,dirty資料會被週期性write-back執行緒寫回介質。換句話說,週期性write-back執行緒每dirty- writeback-centisecs 時間喚醒,然後同步那些dirty_expire_centisecs時間之前就已經dirty的資料。

  3. dity_background_ratio: dirty資料與全部記憶體的最大百分比。當髒資料變多的時候,週期性的write-back執行緒開始同步資料使得髒資料比例變小。這個過程包括那些non-expired的資料。這個可以認為是系統dirty資料的soft limit。

  4. dirty_ratio: dirty資料與全部記憶體的最大百分比,超過這個顯示,writes同步資料先於增加dirty資料。這個是系統diry 資料的hard limit。

  UBIFS write-buffer

  UBIFS是一個非同步檔案系統,和其他檔案系統一樣,UBIFS也利用了page cache。page cache是一個通用的linux記憶體管理機制,page cache可以非常大以cache更多的資料。當你寫一個檔案時,首先寫到page cache中,置為dirty,然後write操作返回(同步的檔案是例外)。以後資料會通過write-back機制寫回。

  write-buffer是UBIFS特定的buffer, 它位於page cache和flash介質之間。這意味著write-buffer 只是寫到write-buffer上 而不是flash介質。

  write-buffer 是用來優化UBIFS在NAND flashes上的速度。 NAND flashes包含NAND pages,頁是NAND flash的最小讀寫單位,通常為512 2KiB或者4KiB。

  write-buffer尺寸等於NAND page size. 他的目的是積累samll writes為一個滿的或者部分滿的page。考慮下面的例子,假定在半秒內寫了4x512bytes nodes,page size是2kiB的情況下。如果沒有write-buffer,那麼分開的寫會使用4個page,浪費6KiB的空間,而write-buffer 可以僅寫一次,並且沒有空間浪費。這意味著,write-buffer避免生成過多的dirty space,UBIFS的垃圾收集所做的工作就越少。

  當然上面的例子是一個理想狀態,即便write-buffer也可能浪費空間,比如使用同步I/O或者資料抵達的時間間隔較大。因為write-buffer也有個過期時間,每3-5秒即便write-buffer不滿,也會寫出。這是為了資料完整性的原因。

  當然,如果UBIFS寫大量的資料,那麼就不使用write-buffer。僅僅資料的最後一部分由於小於NAND page尺寸需要等待填滿page的資料,直到write-buffer的定時器到時。

  write-buffer實現上有一點複雜,UBIFS中使用了一些write-buffer,每個journal head都有一個。當然這並不改變write-buffer的基本機制。

  關於同步有幾點需要注意的:

  1. sycn()會同步所有的write-buffers

  2. fsync(fd)也會同步 fd相關的所有write-buffers

  3. 用O_SYNC開啟檔案,會忽略write-buffers, 所以I/O會直接寫入介質

  4. 如果檔案系統mount時使用了-o sync選項,那麼write-buffers也會被忽略

  在資料同步時要考慮write-buffer的timer時間,在synchronization timout "dirty_expire_centisecs"加上額外的3-5秒,當然由於write-buffer很小,所以delay的資料很少

  UBIFS in synchronous mode vs JFFS2

  mount UBIFS檔案系統使用-o sync標誌,所有檔案系統的操作都變成了同步模式,意味著在file-system操作返回前,所有的資料都寫入了flash介質

  例如,如果你使用write()寫10MB資料到檔案f.data,並且UBIFS是同步模式。在write操作返回前所有的10M檔案資料以及 meta-data(file size, date changes)也寫入了flash介質。此時即便掉電,這個檔案的資料也會包含所有的10MB資料。

  在開啟檔案時,使用O_SYNC標誌也有同樣的效果。

  在突然掉電的情況下,UBIFS並不能像JFFS2那樣提供較多的保證。

  在JFFS2中,所有的meta-data儲存在data node headers中。資料節點包含4KiB的壓縮資料。這意味meta-data資訊在flash有多個版本,每次JFFS2寫一個data node到flash介質上時,都會更新檔案的inode size。當JFFS2mount時,會掃描flash介質,發現最後的data node,同時獲取inode的size

  在實際使用中,JFFS2會順序的寫這10M資料,從頭到尾。如果中間發生掉電,那麼就丟失掉結尾的資料。比如寫10M的資料到JFFS2中,寫了5M,此時掉電發生,那麼可以得到一個5MB的檔案,而丟掉了未寫入的5MB。

  對於UBIFS來說有一點點複雜,資料被儲存在data nodes中,而meta-data被儲存在分離的inode nodes中。meta-data在每個資料節點中沒有像JFFS2一樣有duplicated的版本。UBIFS從來不會寫on-flash inode size外的資料節點。如果不得不寫data node, 並且data node是在on-flash inode size之外(in-memory inodes已經更新為up-to-data尺寸,但是髒的還沒更新到flash上),那麼UBIFS首先要把inode寫入介質,然後才能開始寫資料,此時掉電就可能會產生holes檔案,看下面的例子

  1. 使用者建立一個空檔案f.dat. 檔案是同步的,或者UBIFS檔案系統被mount為同步模式。user呼叫write()函式來寫一個10MB buffer.

  2. kernel首先拷貝所有的10MB資料到page cache。inode size 被修改為10MB並且inode被mark為dirty。沒有任何資料和meta-data寫入flash media,此時如果發生掉電,那麼user看到的是一個空檔案f.data。

  3. UBIFS看到I/O是同步的,那麼首先開始同步inode,首先寫inode node 到flash media中,此時如果發生掉電,那麼user會看到一個10MiB大小的空檔案。

  4. UBIFS開始寫資料,如果掉電發生在這一點上,那麼user看到一個10Mib大小的檔案,在檔案後部是有個hole。

  注意,即便I/O不是同步的,UBIFS在開始寫資料 meta-data到flash media前就返回。write-back將在某個時刻開始寫flash media, 如果在write-back過程發生掉電,那麼同樣會產生hole

  因此,UBIFS並不能像JFFS2那樣,很完美的保留掉電前寫下的資料以及檔案尺寸,主流檔案系統比如ext3也沒有提供JFFS2類似的功能。

  雖然,有時人們用UBIFS替代JFFS2,人們希望UBIFS能夠像JFFS2一樣行事,這是可行的,需要hack一下UBIFS程式碼。之所以UBIFS還沒有這樣實現,是因為這個需求並沒有那麼強烈。或者自己實現它或者說服UBIFS作者實現這個功能。

  Synchronization exceptions for buggy applications

  UBIFS作為一個非同步的檔案系統,application應該在需要的時候同步他們的檔案。這同樣適用於大部分linux 檔案系統

  然而,一些應用忽略了這一點,沒能夠正確的同步檔案。

  Compression

  UBIFS支援快速的壓縮,UBIFS在把資料寫入flash media之前壓縮資料,在讀出來時解壓資料,這個compress/decompress過程對與使用者來說是完全透明的。UBIFS僅僅壓縮檔案資料。目錄,裝置節點等是不壓縮的。Meta-data和indexing資訊也不壓縮的

  當前UBIFS支援LZO和zlib壓縮器。ZLib提供了更好的壓縮率,但是LZO在壓縮和解壓時的速度更快。LZO是UBIFS和mkfs.ubifs的預設壓縮處理器。當然你可以在mkfs.ubifs命令選項中使用-x來disable UBIFS壓縮。

  UBIFS把資料分割為4KiB的塊然後對每一個chunks進行單獨壓縮。這不是最優的,因為大塊的資料壓縮效果更好,即便如此仍然提供了客觀的 flash空間效益。比如,一個真實的root file system映象利用LZO壓縮器可以減小40%空間,而用zlib壓縮器則減少50%空間。這意味如果你可以把一個300MB的檔案系統放到 256MiB的UBI volume中,而且荏苒有100MiB的空閒空間。然而這可能隨著檔案系統存放的內容不同而不同,比如,如果你的檔案系統儲存的都是mp3檔案,UBIFS無法有效的壓縮他們,因為mp3檔案本身就是被壓縮的。

  在UBIFS中是可以針對每個檔案節點來disable/enable壓縮功能的,方法是設定或者清除inode節點的compression標誌。注意,這個壓縮標誌對於目錄是可繼承的,也就是說這個目錄下的檔案和子檔案在建立時,他們的compression標誌是繼承自父目錄

  還要注意的是JFFS2 LZO壓縮和UBIFS的zlib壓縮有輕微不同。UBIFS使用crypto-API縮小方法,而JFFS2直接使用zlib庫。導致UBIFS和 JFFS2使用不同的zlib壓縮選項。名義上,JFFS2使用縮小level3,window bits15,而UBIFS使用level6 windows bit -11(負號使得zlib避免放一個header到資料流中)。經驗顯示JFFS2壓縮率稍微小一些,解壓速度慢一些,但是壓縮速度要快一些。

  Checksumming

  UBIFS寫入到介質的每一片資料都有一個CRC32 checksum。UBIFS使用CRC來保護data和meta-data。每次讀meta-data時,CRC校驗碼都會被驗證。CRC-32校驗是非常強大的,幾乎所有的資料損壞都可以檢查到。UBI也是如此,驗證每一片meta-data。

  data CRC在預設情況下是不驗證的,這能改善檔案系統的讀速度,當然UBIFS可以在mount時用chk_data-crc,來切換為data驗證。這會減少UBIFS的讀速度,但是提供了更好的資料完成性保護。使用這個選項,UBIFS讀取的每一片資訊都會被驗證,任何資料損壞也都會被檢查到。

  注意,當前UBIFS不能disableCRC-32的寫計算,因為UBIFS在recovery過程依賴於它。當從一個unclean reboot恢復,replay日誌時,UBIFS需要檢測broken和寫了一半的UBIFS nodes然後拋棄他們,在這裡UBIFS依賴於CRC-32 checksum

  換句話說,如果你使用UBIFS時disable掉CRC-32,但是你仍然為每一片資料生成了CRC-32 checksum, 你可以隨時啟用讀校驗

  注意:在2.6.39之前default UBIFS行為是相反的, UBIFS預設支援CRC-32, 使用no_chk_data_crc來disable它

  Read-ahead

  read-ahead是一個檔案系統的優化技術,每次讀取的資料比使用者請求的資料多一些。這個主意是基於檔案的訪問一般都是從開始向後順序進行的,所以檔案系統試著把將來可能用到的資料提前讀出了。

  LINUX VFS本身會做這個read-ahead而不需要file system的支援。這對於傳統的塊裝置檔案系統工作良好,但是UBIFS不會得益於此。UBIFS工作於UBI API之上,而UBI工作與MTD API之上,MTD API是同步地, MTD API是非常簡單的,沒有任何request queue。這意味這VFS 阻塞住 UBIFS readers, 讓他們等待read-ahead過程。block-device API是非同步的 readers不需要等待read-ahead.

  VFS read-ahead是為hard drives設計的。但是raw flash裝置和hard drive是非常不同的, raw flash裝置不需要耗時的尋道時間,所以適合hard disk的技術並不適合flash裝置

  也就是說,VFS的read-ahead僅僅會使得UBIFS變慢,所以UBIFS 會disable 掉VFS的read-ahead。但是UBIFS有他自己內部的read-ahead,我們稱之為bulk-read。你可以在mount時增加 “bulk_read”選項來使能bulk-read功能.

  有些flash可能一次讀出全部資料要比分多次讀出資料要快。例如,OneNAND可以做read-while-load如果它讀取超過一個 NAND page。所以UBIFS可以通過一次讀取較大的data chunks來獲取效能上的提升,這就是bulk-read 要做的

  如果UBIFS注意到一個檔案正在被順序的讀取,UBIFS看到接下來的資料就存放在相同的eraseblock中,則UBIFS開始read ahead資料,通過使用大的read request。UBIFS把這些提前讀取的資料放到檔案的cache中,所以使用者的進一步讀取可以直接從cache中獲得。

  很明顯,bulk-read 在某些情況下,可能會減慢UBIFS,所以要小心。此外注意在高度碎片化的檔案系統上,bulk-read並不適合。儘管UBIFS不會主動碎片檔案系統,但是也不會de-fragment檔案系統。比如數序的寫一個檔案,那麼不會有變得碎片化。但是如果你同時寫多個檔案,他們就可能變得碎片化(這也依賴於write-back怎麼樣提交更新),UBIFS不會自動de-frament他們。當然,可以實現一個background的 defragmenter. 我們也可以使用per-inode的日誌頭來避免在一個LEB上混合屬於不同的inode的資料節點。所以仍然有改善的餘地。

  Space for superuser

  UBIFS問超級使用者保留了一些空間,這意味著當檔案系統對於普通使用者為滿時,仍然有一點保留空間給 super-user。其他檔案系統比如ext2也有類似的特點,mkfs.ubifs有一個-R選項可以用來標識保留空間的尺寸。

  預設情況下僅僅root可以使用保留空間,但是可以擴充套件特權使用者的列表。UBIFS可以記錄幾個user和group IDs在超級塊中,允許他們利用保留空間。儘管當前的mkfs.ubifs工具沒有相應的命令列選項,但是很容易實現這個功能。

  注意,UBIFS在mount檔案系統時會輸出保留空間的數目。

  Extended attributes

  UBIFS支援擴充套件屬性: user, truested and security name-spaces. ACL支援還沒實現。

  注意,當前mkfs.ubifs忽略擴充套件屬性,沒有把他們寫入target檔案系統image

  Mount options

  下面是UBIFS-specifi特定的mount選項

  1. chk_data-crc

  2. no_chk_data-crc

  3. bulk_read

  4. no_bulk_read

  此外UBIFS支援標準的sync mount 選項,可以用來disable UBIFS write-back和write-buffer,使得寫過程完全同步。

  注意UBIFS不支援atime,所有atime mount選項不起任何作用。

  Flash space accounting issues

  傳統的檔案系統,比如ext2可以很容易的計算出空閒空間。計算是非常精確的,使用者對此已經習以為常。然後,UBIFS則完全不同了,UBIFS無法彙報它還有多少空閒空間可用。只能彙報最少的可用空間數,這個數目通常會少於實際的數目,有時差錯甚至很大。例如,UBIFS彙報沒有空閒空間,但是仍然可以寫入很多的資料。

  之所以UBIFS彙報的空閒空間的數目少於實際擁有的,是基於以下幾個理由,我們逐一討論。

  Effect of compression

  第一因素是UBIFS的快速壓縮。使用者一般認為檔案系統彙報了N bytes空閒空間,那麼就意味著可以寫入N bytes的檔案資料。因為壓縮的存在,這種想法就不成立了。根據資料壓縮程度的不同,UBIFS可以寫入的資料可能數倍於彙報的空閒空間

  當UBIFS計算空閒空間時,他並不知道即將寫入的資料的特性,因此無法考慮壓縮,所以就假定為最壞的情況,資料沒有壓縮。

  雖然這看起來問題不大。但是綜合考慮壓縮和write-back,壓縮就變成了估算空閒空間的大問題。換句話說,UBIFS無法知道cached dirty資料的壓縮,想知道結果的唯一辦法就是執行壓縮

  Effect of write-back

  假定在page cache中有X bytes的髒資料,他們將在某個時間被刷如flash media。UBIFS當前需要X+O bytes空間來寫這些資料,O是檔案系統開支(比如資料的index,以及每個data node需要一個header)

  問題是UBIFS無法精確的計算出X和O,它使用悲觀的最壞情況的計算,所以當cached data被刷入flash, 所需的flash空間可能原少於需要的X+O,例如看下面的情況

  $ df Filesystem 1K-blocks Used Available Use% Mounted on ubi0:ubifs 49568 49568 0 100% /mnt/ubifs $ sync $ df Filesystem 1K-blocks Used Available Use% Mounted on ubi0:ubifs 49568 39164 7428 85% /mnt/ubifs

  第一次df彙報zero空閒空間,但是sync後彙報了15%的空閒空間。這是因為有很多cached的dirty data, UBIFS為他們保留了所有的空閒空間,但是在flash media過程中,僅使用了部分flash space。

  下面是UBIFS為什麼保留這個多空閒空間的理由

  1. 還是關於compression。資料以uncompressed形式儲存在cache中,UBIFS並不能預估這些資料的壓縮,所以它假定資料完全不能壓縮。然而,現實生活資料是很容易壓縮的,除非.tgz或者.mp3檔案。這就導致了對X的過分估計。

  2. 歸咎於設計,UBIFS nodes不會跨越earseblock的邊界,所以在每個eraseblock的末尾處都會有一些小的浪費空間。這個浪費的flash空間依賴於資料寫入和更改的順序。傳統的UBIFS會悲觀的估計這個浪費空間為最大值,這也導致了對O的過份估計。

  因此UBIFS在同步後可以更精確的估計空閒空間值

  Wastage

  像上面提到的,UBIFS不會跨越LEB邊界。考慮下面數字:

  1. UBIFS node最大尺寸是4256位元組

  2. UBIFS node最小尺寸是56 位元組

  3. 依賴於名字長度,目錄項節點佔據56~304位元組

  4. LEB尺寸:典型的NAND flash 128KiB的物理擦除塊 2048位元組的NAND page,LEB的尺寸是126KiB(或者124KiB如果NAND chip不支援sub-pages)

  因此,如果大部分flash上的節點是非壓縮的data nodes, UBIFS將會浪費126KiB LEB的最後1344bytes。但是現實生活中的資料通常是壓縮的,所以node尺寸可能發生變化,因此在刪除塊末尾的wasted space範圍為0~4255。

  UBIFS把一些小的節點,比如目錄項節點放到LEBs末尾來減少wasted space,但是不理想,UBIFS仍然在LEB的末尾浪費掉大塊的空間。

  當彙報空閒空間時,UBIFS不知道哪種資料,以何種順序寫入flash media。因此,它評估每個LEB的wastage為最大可能值4255。這個計算對大多數現實世界都是太悲觀了,真實的wastage要遠少於 4255。然而,UBIFS彙報給使用者空間程式的是絕對最小值。

  以上意味著LEB越大,UBIFS對空閒空間的預測越準確,比如128KiB擦除塊要好於16KiB的引數塊

  Dirty space

  Dirty space是曾經別UBIFS nodes使用過的空間,但是由於改變或者removed導致這個空間變得無效。例如,如果檔案的內容被re-write,那麼相應的資料node被無效,新的資料data寫入flash media。無效的節點包含dirty space,還有其他的機制會導致dirty space出現。

  UBIFS無法直接重用dirty space。因為相應的flash areas沒有包含所有的0xff bytes。在dirty space可以重用前,UBIFS不得不垃圾收集相應的LEBs。垃圾收集回收dirty space的方法和JFFS2是相同的。請參考JFFS2 design document

  粗略的講,UBIFS垃圾收集選取一個LEB,這個LEB包含一些dirty space,然後把這個LEB上的有效UBIFS節點移到GC reserverd LEB上。這將消耗GC reserved LEB的一些空閒空間,GC選取另外一個需要收集的LEB,然後把有效資料移到這個GC reserved LEB,持續這個過程直到LEB變滿。GC選取另外一個 reserved LEB,繼續這個過程。

  UBIFS有一個概念叫最小I/O 單位, 描述可以寫入flash資料的最小數目。一般情況下UBIFS工作在large-page NAND flash上,最小I/O單位是2KiB

  事實上,UBIFS GC雖然嘗試不浪費任何空間,但是有時並不能如願。UBIFS不一定能夠回收那些dirty空間小於I/O unit的dirty space

  當UBIFS向users彙報空閒空間時,對待dirty space為可用空間,因為GC可能會回收這部分空間為空閒空間,但如上面分析的,UBIFS無法回收所有的dirty space為空閒空間,更壞的情況是UBIFS無法確切知道它可以回收多少dirty space。再一次UBIFS使用最悲觀的計算。

  因此dirty space越少,UBIFS彙報的free space越準確。在實際應用中,這意味著一個滿的檔案系統要比一個空的檔案系統更難預測空閒空間,因為滿的檔案系統有太多的dirty space。

  注意,解決這個問題,UBIFS可以在statfs()中執行GC, GC將把dirty space變為free space, 因此預測空閒空間就變得更精確。然而,這將導致statfs變得非常的慢,因此呼叫statfs的應用的行為將不可預測。此外也可以使用JFFS2使用的 background GC。

  Precise index size is not known

  大概你已經知道,UBIFS在flash media上維護檔案系統的index。index佔據flash的一部分空間。UBIFS journal儲存著FS data。journal中的FS data沒有index, 這意味著on-flash index沒有指向他們。 journal FS data的index是建立在RAM中的,當檔案系統被mount時, UBIFS要掃描整個journal區,然後在RAM中重建index。journal某種程度像是UBIFS中的一個JFFS2檔案系統。

  journal data在提交時會變成indexed。在UBIFS提交時,更新on-flash index,使得index指向journal data。然後UBIFS挑選其他的LEBs作為新的journal, 所以journal在提交後會改變位置。這就是UBIFS的journal特點,日誌區不是固定的。

  UBIFS精確的記錄著index size。也就是,UBIFS一直很清楚當前on-flash index佔據了多少空間。然而,UBIFS無法精確的預測在提交後on-flash index要增加或減少多少。再一次的,UBIFS考慮最壞的情況。

  當然在提交後UBIFS再次知道index的確切尺寸。sync()不僅僅flush左右的髒資料,而且也會提交journal。這意味者同步過的檔案系統對空閒空間的預測更準確。

  事實上,UBIFS本可以精確的預計index size而無須commit操作,但是UBIFS作者並沒有實現它,因為實現很困難,而index size的影響又很小。

  Documentation

  如果檔案系統對你來說是一個完全嶄新的領域,那麼推薦你首先從JFFS2開始,因為UBIFS的很多想法來源於UBIFS。參看JFFS2 design文件

  JFFS3 design 文件,你可以發現很多JFFS2問題的描述,以及很多UBIFS的基本想法。記住,這個文件比較老有些內容過時了。現在我們不再使用JFFS3這個名字,JFFS3已經改名為UBIFS。在寫這個文件時UBI並不存在,當時假定JFFS3直接執行在MTD裝置上。當然JFFS2 overview, JFFS3 Requirements和Introduction to JFFS3章節大部分仍然是正確的並且對UBIFS的想法給了準確的介紹,比如wandering tree和journal。請注意,superblock的描述對UBIFS是不正確的,UBIFS是基於UBI不需要這個技巧。當然,superblock的位置注意可以用於UBI2 layer

  這個文件以及UBIFS FAQ儲存著關於UBIFS的大量資訊。然而你也需要研究UBI,因為UBIFS是依賴於UBI層提供的服務。參考UBI document和UBI FAQ

  UBIFS white-paper 對於新手來說非常的難,所以我們推薦你首先從JFFS3設計文件開始。UBIFS white-paper包含UBIFS設計的總體圖,並且描述了UBIFS的內部。white-paper沒有包含本文件和UBIFS FAQ的一些細節

  最後是UBIFS source code. 程式碼中包含了大量註釋,所以我們推薦你在這裡找到你需要的細節。最後歡迎你到UBIFS mailing list提問

  Raw flash vs. FTL devices

  FTL 全稱 "Flash Translation Layer", 是一個軟體層用來在flash hardware上模擬一個block device。在早期 FTL執行在host computer上,今天FTL通常都是fireware, 執行在儲存裝置內建的控制器上。例如,如果你檢視一個USB flash盤,那麼你可以看到一個NAND chip和一個micro-controller, 這個控制器執行FTL firmware. 一些USB flash deivces甚至有強大的ARM處理器。類似的,MMC, eMMC, SD, SSD和其他的FTL devices有一個內建的處理器來執行firmware。

  所有的FTL設別都提供了一個block I/O訪問介面。雖然這些介面有不同的規範定義的,比如MMC, eMMC, SD, USB mass storage, ATA。但是他們都提供了對塊裝置的訪問。基於塊裝置的訪問意味著整個裝置可以看作是一個線性blocks的排列。每一個塊都可讀可寫。

  儘管大部分常用的flash hardware有FTL, 但是仍讓存在一些裸flash沒有FTL。比如各種手持裝置和嵌入式系統。裸flash裝置和block裝置是非常不同的。他們的工作模式不同,裸flash和block裝置相比,有更多的限制和問題需要軟體考慮這些問題。使用FTL層,這些限制和問題都可以被FTL遮蔽。

  UBIFS檔案系統被設計用來使用raw flash,假定是raw flash裝置模型,他無法工作在block device之上。換句話說,它認為裝置有很多eraseblocks,可以寫入,讀出,或者擦除。UBIFS負責考慮以out-of-place方式寫資料,做垃圾收集等等。UBIFS利用UBI來實現磨損平衡和壞塊管理,正常的block drive都不需要處理這些事情。

  人們經常會問:問什麼有人需要使用raw flash而不是使用eMMC或者類似的塊裝置?這個問題回答起來有點複雜,以下是UBIFS開發者的幾點想法。請記住這個觀點的時間(作者無法保證這些觀點隨著時間的改變而變得不正確),使用者也要根據自己系統的實際情況來考慮這幾點。

  1. 裸NAND 晶片更簡單更便宜,然而,隨著業界推動FTL裝置,情況似乎在發生變化。事實上FTL裝置遠比raw flash裝置複雜,因為FTL不得不內建額外的控制器。但是因為業界生產了大量的FTL,FTL的價格在下降。

  2. 如果你使用一個FTLs裝置,並且打算在上面跑FAT file system。那麼你應該選擇FTL device(eMMC, MMC, SD等等),確保FTL裝置可以正確的執行wear-leveling

  3. 當你想使用FTL裝置作為系統儲存,比如rootfs,並且想使用更可靠的檔案系統比如ext3。在這種情況下,要考慮各種系統需求比如對突然掉電的容忍度?下面的情況大部分是關於系統儲存情形的

  4. FTL devices是一個黑盒。FTL演算法是vendor的祕密,但是你知道NAND flash有磨損平衡,壞塊管理,read-disturb等問題。如果這些問題對你很重要,特別是當使用MLC NAND flash(壽命僅為1000 erase)。因為FTL演算法是封閉的,所有很難確保是否FTL device正確的處理了這些問題。

  5. 如果你開始考慮FTL是怎麼實現的,你可能意識到他必須實現GC。flash hardware需要寫是out-of-place的。但是FTL在通然斷電的情況下工作如何呢,如果通然斷電發生在垃圾收集的過程中呢? FTL device是否保證斷電前的資料不失蹤,不損壞呢?

  6. 掉電tolerance可以被測試,但是磨損平衡和read-disturb很難被測試,因為需要很多時間來測試他們。

  7. 我們聽說過一些flash USB裝置很快的磨損了。比如flash彙報I/O錯誤在經過幾周的密集使用後。這意味著USB device沒有做好wear-leveling,當然這並是說所有的USB device都不好用,而是說你要注意這一點。

  8. 我們聽說一些MMC, eMMC和SD cards在寫的過程掉電導致資料丟失和損壞,甚至在很久以前就存在的資料發生了丟失和損壞。這表示他們的FTL沒有正確處理。再次強調,並不是所有的MMCs/eMMCs SDs有同樣的問題

  9. 回顧歷史,許多FTL裝置使用FAT檔案系統儲存圖片和video。FAT檔案系統不穩定,導致了FTL裝置可能也不穩定。當然丟失幾幅圖片並不是什麼大事,但是確保system libraies不要因為掉電而損壞。

  10. 好的FTL必定是一個複雜的軟體,特別是當FTL去處理MLC NAND。在firmware中實現他們很困難,執行他們需要強大的控制器。我們懷疑一些廠商使用了各種技巧和妥協來使得他們的裝置足夠好和便宜。例如,眾多周知有些廠商針對FAT優化他們FTL,但是如果你在他們上面使用ext3,那麼你就可能會碰到很多不可預知的問題,裝置可能會出乎意料的變化,當然,對於封閉的FTL你沒有辦法驗證

  11. SSD驅動和eMMC MMC/SD是非常不同的。他們很昂貴,而且使用強大的CPU來運行復雜的軟體,因此他們可能工作的很好

  12. FTL裝置變得越來越便宜,同時也越來越好,儘管我們很難界定一個裝置是好還是壞。通常來說使用FTL沒什麼不對,因為你相信它,也測試過它,或者僅僅你的需求很適合用FTL

  13. 使用raw flash的好處是,你知道你正在做什麼,UBI/UBIFS處理NAND flash的所有事情,包括bad erase-blocks和wear-leveling。它保證掉電處理。同時它是開放的,你可以驗證,測試,修改它。沒有隱藏它能做什麼不能做什麼。另一方面,使用FTL你沒有辦法看到內部正在發生的事,FTL廠商可能騙你說他們的FTL裝置有多好。當你發現一個bug時,開發商也不一定很快的給予反饋。

  14. 理論上,UBIFS可以更好的工作因為他比FTL知道更多的資訊,例如,UBIFS知道刪除的檔案,而FTL不知道,所以FTL可能為已經刪除的檔案保留 sectors。然而,一些FTL裝置可能支援"discard"請求,所以能處理來自檔案系統的提示。無論如何,UBIFS在裸的NAND可以更好的工作。當然,FTL裝置可以包括多個NAND chips,這就有了並行的可能來提高I/O速率

  15. 很明顯,FTL的優點是你可以在它上面使用老的可信的軟體。但是注意,有時這並不完全正確。UBIFS作者曾經測試過一個知名的eMMC,在掉電時產生了嚴重的問題。此外,這個eMMC不能和ext3很好的配合,因為當這個FTL掉電時,會出現sectors不可用,讀操作會返回ECC錯誤。對於ext3 來說read errors是致命的,因為檔案系統沒有考慮這種情況。fsck.ext3工具也沒法修理彙報read errors的檔案系統。

  所以很難有一個標準答案。在你的系統需求和決定中考慮正反因素。無論如何,raw flashed在嵌入式世界中廣泛使用,這也是為什麼開發UBIFS的原因。