虛擬記憶體與記憶體映射

mmap 與記憶體映射檔案

/ EM-map /

通常你讀一個檔案是一再呼叫 read(),把位元組複製進緩衝區。mmap() 提供另一種做法:它讓一個檔案(或一塊全新的空白記憶體)直接以你定址空間中一塊區域的形式出現,於是你用一般的指標存取就能碰到資料,而不是用 read/write 呼叫。它就像把一份文件釘在你自己的桌上,讓你就地閱讀與編輯,而不必每次都打電話給檔案室要下一頁。

具體來說,mmap() 請作業系統建立一段虛擬位址範圍的分頁表項,並用一個檔案(記憶體映射檔案,memory-mapped file)或不特定的東西(匿名對映,anonymous mapping,當作原始記憶體用)來支撐它們。它回傳一個指向該區域開頭的指標。對檔案對映而言,那些頁是透過一般的分頁錯誤按需從檔案填入:你第一次碰一頁時,作業系統就把檔案的那一部分讀進來;若對映是共享且可寫的,你的寫入會回寫到檔案。對匿名對映而言,那些頁是需求零頁記憶體——這正是 malloc() 實作在底層向核心取得大區塊的方式。

為什麼要用它?對檔案而言,mmap() 在隨機存取時可能更快也更簡單,並讓多個行程便宜地共用一份檔案頁的實體副本。對記憶體而言,匿名 mmap() 是大型配置底下的低階基礎元件。誠實的提醒:記憶體映射檔案仍會做真正的磁碟 I/O,只是藏在分頁錯誤裡,所以一次碰觸可能意外地阻塞;對映不會改變檔案的大小;而存取超過檔案實際長度的對映會引發錯誤。mmap() 是有威力但帶利刃的工具,不是免費的加速。

以唯讀方式對映一個檔案,再把它當陣列用:char *p = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); 現在 p[0]、p[1]…… 直接讀檔案的位元組,每一新頁在第一次碰觸時從磁碟被錯誤帶進來,完全不需明確的 read() 呼叫。

檔案變成可用指標定址;錯誤在幕後完成 I/O。

記憶體映射檔案並非神奇地已在 RAM 裡——碰觸一頁仍會觸發一次(可能是主要的)分頁錯誤與真正的磁碟 I/O,所以一次存取可能阻塞。而存取超過檔案實際長度的對映會引發錯誤;mmap() 不會把檔案撐大。

又称
mmap()memory-mapped fileanonymous mapping記憶體映射