檔案與輸入輸出

清出、同步與持久性

/ fsync -> EFF-sink /

當你「儲存」一個檔案時,資料究竟最後落到哪裡——而它在哪個時間點才安全到不怕停電?從你的程式到旋轉的碟片或快閃晶片,這段旅程上有好幾站,而「我呼叫了 write」並不等於「它已安穩在磁碟上」。理解這些層次,正是讓你在機器於最糟時刻當掉時不丟失資料的關鍵。

在你的資料與持久性之間,通常有三層。第一,若你用的是緩衝 stdio,位元組可能還躺在函式庫的使用者空間緩衝區裡;fflush(stream) 把它們推出那個緩衝區、進入核心。第二,一旦進了核心,位元組就坐在核心的頁面快取裡——作業系統已經接受了它們,但為了速度,不一定已把它們寫到實體裝置;write() 已經返回,資料卻仍只在 RAM 中。第三,fsync(fd) 告訴核心真的把那個檔案的資料從頁面快取沖刷到儲存裝置、並等到裝置確認——只有在 fsync 返回之後,資料才對當機或停電具有持久性。另外還有 fdatasync(沖刷資料但略過部分元資料),而對於目錄項目,你可能還需要對目錄也做 fsync,好讓新建檔案的名字得以存活。

為何重要:這就是「看起來存好了」與「真的存好了」之間的差別。資料庫、編輯器,以及任何不能弄丟你工作成果的東西,都會在對的時機呼叫 fsync,並接受 fsync 很慢(它要等真實硬體)這件事,正因為持久性是有代價的。常見的誤解是:write() 返回就代表資料在磁碟上了——並沒有;它只代表核心拿到了。清出 stdio 緩衝區(fflush)與同步到裝置(fsync)是兩個不同層次的兩個不同步驟,要得到保證,你兩者都需要。

fputs(line, f); /* 1. 位元組可能在 stdio 緩衝區裡 */ fflush(f); /* 2. 把它們從緩衝區推進核心 */ fsync(fileno(f)); /* 3. 強迫核心把它們寫到裝置,現在才持久 */

三層,三步:stdio 緩衝區 -> 核心頁面快取 -> 實體裝置。持久性需要這三者全部。

write() 返回並不代表資料已在實體磁碟上——它只代表核心已把它收進快取。要對當機具持久性,需要 fsync(或 fdatasync);單靠 fflush 只是把 stdio 緩衝區清進那個同樣易失的快取裡。

又稱
fsync and durabilitydata durabilityfsync 與資料持久性