頁面快取(page cache)
磁碟比 RAM 慢上數千倍,而程式又一遍又一遍地讀同樣的檔案——同一個共享函式庫、同一份設定、同樣的熱門資料庫頁。每次都從磁碟取它們會是瘋狂之舉。所以核心把最近用過的檔案資料留在 RAM 裡,並從那裡服務重複的讀取。那個位於 RAM 的檔案內容儲存就是頁面快取,在一台忙碌的機器上,它悄悄地成為記憶體最大的單一使用者。
頁面快取以頁大小的區塊(通常 4 KiB)持有檔案資料,以「來自哪個檔案、哪個偏移」為鍵。任何東西第一次讀取一個檔案的某區域時,核心把它從磁碟複製進一個空閒頁、並遞給程式一份副本;該頁隨後保持快取。下一次讀取同一個區域——無論是同一個程式還是任何別的——都從 RAM 服務,完全不存取磁碟。寫入則反方向走:一個普通的 write() 更新 RAM 裡快取的頁、把它標為髒(已改但尚未落磁碟),立刻返回你的程式;核心稍後把髒頁回寫到磁碟。一個記憶體映射檔案(mmap)把這些同樣的快取頁直接映射進你的定址空間,所以讀取映射與讀取檔案共享同一份資料副本。歷史上 Unix 有一個分離的緩衝快取存放原始磁碟區塊;現代 Linux 把它們統一了,所以頁面快取同時持有檔案頁與區塊緩衝。
為何重要:頁面快取正是為何一次建構的第二次執行、或一個程式的第二次載入,遠快於第一次——資料已在 RAM 裡。空閒 RAM 在忙碌的伺服器上看起來嚇人,但大半是頁面快取,核心會立刻把它丟掉以滿足真正的配置,所以被快取的記憶體是可用的,而非浪費掉的。誠實的代價:因為一次成功的 write() 只抵達頁面快取,你的資料在回寫或一次明確的 fsync() 把它沖刷到穩定儲存之前,都在揮發性的 RAM 裡——頁面快取既是寫入之所以快的原因,也是它們之所以尚未持久的原因。
首次 讀取 file.dat 偏移 0 -> 未命中,磁碟讀取,複製進一個快取頁 二次 讀取 file.dat 偏移 0 -> 命中, 從 RAM 服務,無磁碟 I/O write() 到 file.dat -> 更新快取頁、標為髒、現在就返回 (髒頁只在回寫或 fsync() 時抵達磁碟)
重複讀取命中頁面快取、跳過磁碟;寫入只更新快取並把頁標為髒。
一次成功的 write() 不代表「已落磁碟」——它代表「在頁面快取裡」。位元組住在揮發性 RAM 裡,直到回寫或 fsync() 使它們持久,所以 write() 剛返回後斷電仍可能丟失它們。free 輸出裡偏高的 cached 記憶體是可回收的頁面快取,而非短缺。