檔案系統要解決的一個關鍵問題是怎樣防止掉電或系統崩潰造成資料損壞,在此類意外事件中,導致檔案系統損壞的根本原因在于寫檔案不是原子操作,因為寫檔案涉及的不僅僅是用戶資料,還涉及元資料(metadata)包括 Superblock、inode bitmap、inode、data block bitmap等,所以寫操作無法一步完成,如果其中任何一個步驟被打斷,就會造成資料的不一致或損壞,
舉一個簡化的例子,我們對一個檔案進行寫操作,要涉及以下步驟:
(1)從data block bitmap中分配一個資料塊;
(2)在inode中添加指向資料塊的指標;
(3)把用戶資料寫入資料塊,
如果步驟2完成了,3未完成,結果是資料損壞,因為該檔案認為資料塊是自己的,但里面的資料其實是垃圾;
如果步驟2完成了,1未完成,結果是元資料不一致,因為該檔案已經把資料塊據為己有,然而檔案系統卻還認為該資料塊未分配、隨后又可能會把該資料塊分配給別的檔案、造成資料覆寫;
如果步驟1完成了、2未完成,結果就是檔案系統分配了一個資料塊,但是沒有任何檔案用到這個資料塊,造成空間浪費;
如果步驟3完成了,2未完成,結果就是用戶資料寫入了硬碟資料塊中,但白寫了,因為檔案不知道這個資料塊是自己的,
日志檔案系統(Journal File System)就是為解決上述問題而誕生的,
它的原理是在進行寫操作之前,把即將進行的各個步驟(稱為transaction)事先記錄下來,保存在檔案系統上單獨開辟的一塊空間上,這就是所謂的日志(journal),也被稱為write-ahead logging,日志保存成功之后才進行真正的寫操作、把檔案系統的元資料和用戶資料寫進硬碟(稱為checkpoint),這樣萬一寫操作的程序中掉電,下次掛載檔案系統之前把保存好的日志重新執行一遍就行了(術語叫做replay),避免了前述的資料損壞場景,
有人問如果保存日志的程序中掉電怎么辦?最初始的想法是把一條日志的資料一次性寫入硬碟,相當于一個原子操作,然而這并不可行,因為硬碟通常以512位元組為單位進行操作,日志資料一超過512位元組就不可能一次性寫入了,所以實際上是這么做的:給每一條日志設定一個結束符,只有在日志寫入成功之后才寫結束符,如果一條日志沒有對應的結束符就會被視為無效日志,直接丟棄,這樣就保證了日志里的資料是完整的,
一條日志在它對應的寫操作完成之后就沒用了,占用的硬碟空間就可以釋放,保存日志的硬碟空間大小是有限的,被回圈使用,所以日志也被稱為circular log,
至此可以總結一下日志檔案系統的作業步驟了:
Journal write : 把transaction寫入日志中;
Journal commit : 在一條日志保存好之后,寫入結束符;
Checkpoint : 進行真正的寫操作,把元資料(metadata)和用戶資料(user data)寫入檔案系統;
Free : 回收日志占用的硬碟空間,
以上方式把用戶資料(user data)也記錄在日志中,稱為Data Journaling,Linux EXT3檔案系統就支持這種方式,這種方式存在效率問題:
就是每一個寫操作涉及的元資料(metadata)和用戶資料(user data)實際上都要在硬碟上寫兩次,一次寫在日志里,一次寫在檔案系統上,元資料倒也罷了,用戶資料通常比較大,拷貝幾個GB的電影檔案也要乘以2實在是降低了效率,
一個更高效的方式是Metadata Journaling,不把用戶資料(user data)記錄在日志中,它防止資料損壞的方法是先寫入用戶資料(user data)、再寫日志,即在上述”Journal write”之前先寫用戶資料,這樣就保證了只要日志是有效的,那么它對應的用戶資料也是有效的,一旦發生掉電故障,最壞的結果也就是最后一條日志沒記完,那么對應的用戶資料也會丟,效果與Data Journaling丟棄日志一樣,重要的是檔案系統的一致性和完整性是有保證的,
Metadata Journaling又叫Ordered Journaling,大多數檔案系統都采用這種方式,像Linux EXT3檔案系統也是可以選擇Data Journaling還是Ordered Journaling的,
另外如果你想更好的提升你的編程能力,學好C語言C++編程!彎道超車,快人一步!
C語言C++編程學習交流圈子,QQ群1030652847【點擊進入】微信公眾號:C語言編程學習基地
分享(原始碼、專案實戰視頻、專案筆記,基礎入門教程)
歡迎轉行和學習編程的伙伴,利用更多的資料學習成長比自己琢磨更快哦!
編程學習書籍分享:

編程學習視頻分享:
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/227211.html
標籤:C++
上一篇:標準C++的型別轉換符:static_cast、dynamic_cast、reinterpret_cast和const_cast
下一篇:關于資料庫鏈接
