回寫(writeback,髒頁)
當你的程式寫入一個檔案,位元組通常不會立刻去磁碟——它們落進 RAM 的頁面快取,程式則一路飛奔而去。但那些「在 RAM 中被改過」的頁終究必須抵達磁碟,否則改動會在關機時消失。在背景找出已被修改卻尚未存檔的頁、並把它們寫出到儲存的過程,就叫做回寫。一個在快取中被修改、卻尚未寫到磁碟的頁,叫做髒頁。
以下是其生命週期。一個 write() 把受影響的頁標為髒、立刻返回——便宜又快。核心追蹤存在多少髒頁、它們有多老。專責的回寫執行緒(歷史上叫 pdflush/bdflush,現在是每裝置的 flusher 執行緒)週期性醒來、把髒頁寫到磁碟,每落下一個就清掉它的髒旗標。回寫也在記憶體中太多部分變髒時被觸發(一個可調的門檻,dirty_ratio):一旦髒的比例變高,核心便強制回寫,甚至可能節流一個弄髒頁的速度快過磁碟能吸收的行程,使一個失控的寫手無法用未存資料填滿整個 RAM。這樣批次化寫入,讓核心能把對同一區域的許多小寫合併、為磁碟排序它們、甚至取消對「在離開 RAM 前又被覆寫」之資料的寫入。
為何重要:延遲回寫正是寫入感覺起來瞬時的原因,也是一連串小寫不會用細碎 I/O 猛敲磁碟的原因。誠實的代價是一個持久性窗口:在你的 write() 返回與回寫真正完成之間,你的資料只存在於揮發性 RAM、當機即失。更糟的是,回寫可能在 write() 已經回報成功「之後」才失敗(磁碟滿了、磁區壞了、裝置斷線)——錯誤只在稍後、在 fsync() 或 close() 時才浮現,這正是為何忽略 fsync() 的回傳值是一個真正的錯誤,而非吹毛求疵。
write(fd, ...) -> 頁標為髒,立刻返回 ... 時間流逝 ... flusher 執行緒把髒頁寫到磁碟 -> 頁變乾淨 或:fsync(fd) -> 現在就強制把這個檔案所有髒頁寫出、並回報任何錯誤
寫入只是弄髒一個快取頁;背景回寫(或 fsync)才是真正把它搬到磁碟的東西。
回寫可能在 write() 已回報成功「之後」失敗——磁碟滿、壞磁區或拔掉的裝置只在 fsync() 或 close() 時才讓錯誤浮現。在某些系統上,失敗的回寫還會清掉髒旗標,使日後的 fsync() 可能錯誤地回報成功(歷史上的 fsync 可能丟資料的錯誤);穩健的應用會檢查 fsync 的回傳、並把它的失敗當成資料遺失。