虛擬記憶體與需求分頁

記憶體映射檔案(memory-mapped file)

想像不再是請一位櫃員隔著櫃台一行一行唸文件給你聽,而是櫃員直接把整份文件攤開放在你自己的桌上,說「它歸你了,任何部分都可以直接讀或改。」你不再為每一行提出請求;你就看著面前的這一頁,而你在桌上做的任何更改稍後都會反映回那份正式檔案。記憶體映射檔案為程式做的正是這件事:它把一個檔案直接放進程式的位址空間,使得讀寫該檔案變成普通的讀寫記憶體。

具體來說,像 mmap 這樣的系統呼叫,把一個檔案(或它的一部分)映射到行程虛擬位址空間的一段範圍裡。在那之後,檔案的位元組就以記憶體的樣貌出現:映射區域中的位址 X 對應到檔案中的某個特定偏移。程式正常地讀寫那段記憶體,每次存取都不需要明確的讀或寫系統呼叫。在幕後,這純粹是需求分頁:當程式第一次觸碰映射區域的某一頁,一次分頁錯誤就把檔案的那一部分從磁碟帶進一個頁框;當一個被寫過(髒)的頁被趕走、或映射被沖寫時,作業系統就把它寫回檔案。由於頁面快取同時支撐兩者,映射的檔案頁與核心的檔案快取是同一批頁,因此在獨立的 I/O 緩衝區與程式之間不需要額外的複製。

為什麼重要:記憶體映射讓檔案 I/O 既快又方便——你用普通的指標或陣列運算就能操作檔案內容,而作業系統會延遲且高效地處理載入與儲存。它也促成了一種乾淨的共享記憶體行程間通訊形式:若兩個行程以共用映射方式映射同一個檔案,它們會透過各自的記憶體看見彼此的寫入,因為兩者都映射到同一批底層的頁面快取頁。誠實的提醒是:錯誤可能以一次錯誤(一個信號)而非讀或寫的回傳值的形式出現,這較難處理;映射一個檔案並不保證資料已在磁碟上,除非它被沖寫;而對於大小變動很大的檔案,映射也很笨拙。

一個程式映射一個 2 GB 的日誌檔案,並把讀取第 1,500,000,000 個位元組單純寫成 mapped[1500000000]。只有含那個位元組的那一個 4 KB 頁從磁碟被分頁進來;其餘約 2 GB 在被參考之前都原封不動地留在磁碟上。

檔案存取變成記憶體存取,一次一頁地延遲分頁進來。

對同一檔案的共用映射是一條真正的行程間通訊管道——兩個行程實實在在地共用底層的頁。但私有(寫入時複製)映射則不然:每個行程的寫入都維持私有、彼此看不見,而且可能根本不會被寫回檔案。

又稱
mmapfile mapping記憶體映射mmap 映射