硬碟寫到一半時斷電,檔案系統裡會發什麼?

發表於2017-02-22

硬碟寫到一半時斷電,檔案系統裡會發什麼?

【伯樂線上轉註:】本文源自知乎問答帖:《硬碟寫到一半斷電時檔案系統發生了什麼?》。原題主的問題補充:

斷電時檔案系統發生了什麼?硬碟又發生了什麼?下一次開機時寫到一半的檔案在系統層面還在嗎?在底層還在嗎?

更進一步的, 檔案系統如何保證事務性, 會不會存在某種極端情況導致例如最後幾個bit還沒寫完, 檔案系統卻認為它成功了的情況?

回答不限任何檔案系統,謝謝!

下面是「北極」的回覆分享,伯樂線上已獲授權:

斷電的一瞬間,很多事情是無法確定的:

1. 你無法確定你試圖向裝置驅動傳送的寫指令是否成功,驅動程式本身一般都有快取

2. 即使寫指令正常返回,你也無法確定裝置實際上是否寫成功,因為裝置本身可能也有快取。目前沒有裝置能保證寫指令返回的情況下,所有資料一定成功的儲存在介質上(但部分廠商能保證少量資料一定能成功寫入),對儲存裝置的flush操作並非絕對可靠

3. 哪些成功哪些失敗可能是亂序的,換句話說,如果先傳送寫請求A,再傳送寫請求B,並且都成功返回,掉電時請求A可能丟失,但B成功(NCQ功能);

4. 機械式磁碟可能會出現丟失半截資料的情況(比如,一個512位元組扇區只寫入了100位元組,也就是題主說的bit級錯誤),但這種一般都會通過校驗位檢測出來。

因為有以上這麼多的限制,實際上檔案系統一般沒辦法保證資料一定不丟失,甚至哪些丟失哪些能恢復也是不確定的。

一般來說,檔案系統有以下的幾種策略:

1. 完全不管錯誤的事情,錯了就錯了;

2. 打標記位的方式,如果懷疑有錯,通過磁碟檢測功能恢復;

3. 在設計上保證檔案系統結構上可恢復,但不保證使用者資料可恢復;

4. 能在使用者資料層面上保證資料的絕對正確。

第一種和第二種策略現在比較少見,FAT檔案系統算是屬於這類;主流檔案系統基本上都能保證第三種,比如NTFS之類的;第四種比較難,一般都要配合儲存驅動一起,多見於Flash介質的專屬檔案系統。

保證資料不損壞,具體的方案一般有:

方案1:Copy-On-Write,寫資料的時候不在原來的位置寫,而是先讀一份,然後寫到另外一個位置,當確認寫成功時,把檔案系統的指標指向新的位置。如下圖:

硬碟寫到一半時斷電,檔案系統裡會發什麼?

實際應用中,比這個情況複雜,因為Data2寫入的過程中,File1本身的一些資訊(修改時間等)也發生了變化,所以CopyOnWrite產生的影響不止這一個塊,而是很多。

方案2:日誌(Journal)技術。使用日誌記錄meta-data甚至是資料塊的變化情況(NTFS就是這種策略),一旦出現掉電情況,在日誌中反推到一個正確的狀態上,就可以保證meta-data不損壞。

常見的方案就這兩種,當然還有別的更復雜的技術,可以參考這個連結(Comparison of file systems),但不管用什麼方案,本質上都是以犧牲效能為代價換取結構上的穩定。

最後回到題主的問題,檔案系統如何保證資料的正確性?如果是指檔案的資料部分,是無法保證的,因為檔案系統無法確定資料到底寫沒寫進去,絕大多數檔案系統只能保證自身結構是正確的,但這個正確可能是回滾之後的狀態,具體回滾多少內容,檔案系統自己也不能保證。

這事說起來挺複雜的,不同檔案系統,不同裝置,不同介質,效果都是有區別的。

 

下面是「馬濤」的回答分享,伯樂線上已獲授權

檔案系統的設計一般是效能和資料完整性的妥協,如果你想要最高完整性,那麼效能會差一些,如果你想要最高效能,那麼資料完整性會差一些,僅此而已。當然這些都是針對資料而言的,對於檔案系統自身的後設資料,一般設計者為了自身資料完整性的考慮都會有日誌系統(比如ext4使用jbd2),從而儘量保證檔案系統在斷電時沒有問題或者問題很少(需要使用fsck來解決),但是使用者資料是否完備是由使用者選擇和配置的,而檔案系統會根據不同配置選擇不同的策略。

我們以Linux最通用的ext4為例,他有三種模式供使用者選擇data=writeback/ordered/journal,對應的是資料落盤的三種方式,writeback是指後設資料更新的時候資料不考慮是否落盤,所以掉電以後可能會看到一些亂七八糟的資料,ordered的意思是後設資料更新在資料更新之後(如果你沒有更改後設資料那就沒有辦法保證資料了),還有一種是journal意思是資料也先寫journal再寫檔案(double write),這種資料安全性最高,當然效能也最差。詳細的介紹可以參加mount(8)。

說完檔案系統,我們再說硬碟,硬碟在檔案系統下面,所以檔案系統是重度依賴硬碟來實現資料完整性的,而硬碟也提供了一些命令來保障檔案系統資料完整性的語義。比如硬碟會提供flush命令,保證只要上層檔案系統呼叫了這個命令,那麼檔案系統之前寫到硬碟裡面的內容必須落盤了(一般的硬碟有記憶體cache,為了提高寫入效能會快取一部分資料,flush會命令硬碟將cache內容落盤。當然如果硬碟如果有電容可以保證cache即使掉電也會落盤,那麼他也可以欺騙上層的檔案系統 :) ),這樣檔案系統在寫入一些關鍵資料以後必須呼叫flush,在得到硬碟的flush反饋以後再進行後面的工作。當然現代硬碟還有一些FUA(Force Unit Access)之類的操作,這些是為了加速某類磁碟落盤的操作,本質上即使硬碟不提供檔案系統可以改成write+flush來實現(只是效能差一些),如果感興趣大家可以自行google之。

說完底層,還有一層要說一下,就是你的應用是怎麼寫檔案和硬碟的,如果是buffer write(應用只寫到作業系統的記憶體,由作業系統延遲迴寫到硬碟),那麼很大可能你在掉電之前一段時間寫入的資料都會不見了(作業系統還沒有回寫),如果應用是direct IO(應用繞過作業系統記憶體,直接寫硬碟),那麼可能只有掉電時刻正在寫入的資料不見了,當然這裡可能還涉及到direct IO的語義以及不同檔案系統的具體實現,和檔案系統相關,就需要具體問題具體分析了。

相關文章