內存映射文件

內存映射文件（Memory-mapped file），或稱“文件映射”、“映射文件”，是一段虛擬內存逐字節對應於一個文件或類文件的資源，使得應用程序處理映射部分如同訪問主內存。

**得益**
主要用處是增加I/O性能，特別是用於大文件。對於小文件，內存映射文件會導致碎片空間浪費，因為內存映射總是要對齊頁邊界，最少耗費4 KiB。故一個5 KiB的文件將會映射佔用8 KiB內存，浪費了3 KiB內存。訪問內存映射文件比直接文件讀寫要快幾個數量級。 
內存映射文件可以只加載一部分內容到用戶的邏輯內存空間。這對非常大的文件特別有用。
使用內存映射文件可以避免顛簸：把相當大的文件直接加載到內存時，由於可用內存不足，使得一邊讀取文件內存，同時把部分已經加載的文件從內存寫入硬盤虛存文件中。
內存映射文件由操作系統的內存管理程序負責，因此繞過了硬盤虛存的分頁文件（page file）。

**分類**
有兩類內存映射文件：

**Persisted**
Persisted文件與硬盤文件相關聯，當關閉內存映射時，數據被寫入對應的硬盤文件中。適合於很大的文件。

**Non-persisted**
Non-persisted文件並不關聯於硬盤文件。當關閉內存映射文件，所有數據被拋棄。適用於創建進程間通信的共享內存。
對於Windows操作系統，不需要調用CreateFile。調用CreateFileMapping時，將INVALID_HANDLE_VALUE作為hFile參數傳入，指示創建的文件映射對象不是磁盤上的文件，而是頁交換文件。所需分配的存儲器大小由CreateFileMapping的dwMaximumSizeHigh和dwMaxinumSizeLow參數決定。

**缺點**
內存映射文件需要在進程的佔用一塊很大的連續邏輯地址空間。對於Intel的IA-32的4 GiB邏輯地址空間，可用的連續地址空間遠遠小於2---3 GiB。
相關聯的文件的I/O錯誤（如可拔出驅動器或光驅被彈出，磁盤滿時寫操作等）的內存映射文件會嚮應用程序報告SIGSEGV/SIGBUS信號（POSIX環境）或EXECUTE_IN_PAGE_ERROR結構化異常（Windows環境）。通常的內存操作是無需考慮這些異常的。
有內存管理單元（MMU）才支持內存映射文件。

**用途**
最常見用途是絕大多數操作系統(包括Microsoft Windows與Unix-like系統)用於加載進程。
另一個用途是多個進程的共享內存。 
第三個用途是對大文件的讀寫。

**支持的平臺**
一些可移植的庫實現：- Boost.Interprocess, 在Boost C++ Libraries- Boost.Iostreams,也在Boost C++ Libraries- Fmstream- Cpp-mmf
Java語言的FileChannel.
D語言的標準庫（std.mmfile module）
Ruby語言的gem（庫）Mmap.
Python語言的mmap標準庫模塊.  
Perl的Sys::Mmap或File::Map.
Microsoft .NET的P/Invoke，或者Managed access(參見  Memory-Mapped Files date=20180614131336 ). 或第三方庫API.
PHP的庫函數file_get_contents()(  revision log date=20160118143340 ).
R語言的一個庫 bigmemory date=20201112033203 使用了Boost庫的實現.
J語言至少自從2005年開始支持內存映射文件。它包括了對盒裝的陣列數據和單一數據類型文件的支持。支持可以從'data/jmf'加載。J的Jdb和JD數據庫引擎使用內存映射文件用於列存儲。

**類Unix**
POSIX函數mmap(),創建一個內存映射文件，需要提供文件描述符、開始位置的文件指針、映射長度等參數。 or OpenVMS

**Windows**
Windows API提供了一組函數以實現內存映射文件。
*創建或打開文件內核對象HANDLE CreateFile(PCSTR pszFileName,DWORD dwDesiredAccess,DWORD dwShareMode,PSECURITY_ATTRIBUTES psa,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile); 要實現內存文件的修改即時轉換為磁盤文件的修改，對文件CreateFile時必須寫權限。Windows加載PE文件時，會保證寫時複製機制的使用。寫時複製機制指的是同一個可執行文件的多個進程時，當其中一個進程要改寫其映射文件的數據時，Windows會開闢一個新的頁文件把要修改頁的內容複製到該新頁文件中並將新的頁文件與當前進程相關聯。
**dwDesiredAccess的值
***0：不能讀取或寫入文件的內容。用於僅需要獲得文件的屬性時
***GENERIC_READ：可以從文件中讀取數據 
***GENERIC_WRITE：可以將數據寫入文件 
***GENERIC_READ|GENERIC_WRITE：可以從文件中讀取數據，也可以將數據寫入文件 
**dwShareMode的值
***0：打開文件的任何嘗試均將失敗
***FILE_SHARE_READ：使用GENERIC_WRITE打開文件的其他嘗試將會失敗
***FILE_SHARE_WRITE：使用GENERIC_READ打開文件的其他嘗試將會失敗
***FILE_SHARE_READ|FILE_SHARE_WRITE：打開文件的其他嘗試將會取得成功
*創建（或打開）一個文件映射內核對象HANDLE CreateFileMapping(HANDLE hFile,PSECURITY_ATTRIBUTES psa,DWORD fdwProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,PCTSTR pszName);
**第一個參數hFile標識想要映射到進程地址空間中的文件句柄。該句柄由前面調用的CreateFile函數返回。
**第二個參數psa參數指向文件映射內核對象的SECURITY_ATTRIBUTES結構的指針，通常傳遞的值是NULL（它提供默認的安全特性，返回的句柄是不能繼承的）。
**第三個參數fdwProtect設定保護屬性：
***PAGE_READONLY：當文件映射對象被映射時，可以讀取文件的數據。必須已經將GENERIC_READ傳遞給CreateFile函數
***PAGE_READWRITE：當文件映射對象被映射時，可以讀取和寫入文件的數據。必須已經將GENERIC_READ | GENERIC_WRITE傳遞給CreateFile
***PAGE_WRITECOPY：當文件映射對象被映射時，可以讀取和寫入文件的數據。如果寫入數據，會導致頁面的私有拷貝得以創建。必須已經將GENERIC_READ或GENERIC_WRITE傳遞給CreateFile
***SEC_NOCACHE：節保護屬性，告訴系統沒有將文件的任何內存映射頁面放入高速緩存
***SEC_IMAGE：節保護屬性，映射的文件是個PE文件映像。系統確定PE文件各節的保護屬性賦予文件映像的各個頁面。例如， PE文件的代碼節（.text）通常用PAGE_EXECUTE_READ屬性， 而PE文件的數據節(.data)通常用PAGE_READWRITE屬性。 
***SEC_RESERVE：節保護屬性
***SEC_COMMIT：節保護屬性 
**第四和五個參數dwMaximumSizeHigh和dwMaximumSizeLow：該文件的最大字節數
**第六個參數pszName：以0結尾的字符串，給該文件映射對象賦予一個名字。該名字用於與其他進程共享文件映射對象。
*文件數據映射到進程地址空間並提交PVOID MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,SIZE_T dwNumberOfBytesToMap);
**第一個參數hFileMappingObject標識文件映射對象的句柄，該句柄是前面調用CreateFileMapping或OpenFileMapping函數返回的。
**第二個參數dwDesiredAccess標識如何訪問該數據:
***FILE_MAP_WRITE：可以讀取和寫入文件數據。CreateFileMapping函數必須通過傳遞PAGE_READWRITE標誌來調用
***FILE_MAP_READ：可以讀取文件數據。CreateFileMapping函數可以通過傳遞下列任何一個保護屬性來調用：PAGE_READONLY、PAGE_ READWRITE或PAGE_WRITECOPY
***FILE_MAP_ALL_ACCESS：與FILE_MAP_WRITE相同
***FILE_MAP_COPY：可以讀取和寫入文件數據。如果寫入文件數據，可以創建一個頁面的私有拷貝。CreateFileMapping函數可以用PAGE_READONLY、PAGE_READWRITE或PAGE_WRITECOPY等保護屬性中的任何一個來調用。 
***第三、四個參數dwFileOfsetHigh和dwFileOfsetLow：指定從哪個字節開始作為視圖中的第一個字節來映射。
***第五個參數dwNumberOfBytesToMap指出多少字節要映射到地址空間。如果設定的值是0，那麼系統將設法把從文件中的指定位移開始到整個文件的結尾的視圖映射到地址空間。
*從進程地址空間撤消文件映射BOOL UnmapViewOfFile(PVOID pvBaseAddress);
**參數pvBaseAddress由MapViewOfFile函數返回。
*強制系統將修改過的數據重新寫入磁盤BOOL FlushViewOfFile(PVOID pvAddress,SIZE_T dwNumberOfBytesToFlush);
**第一個參數是內存映射文件視圖的一個字節的地址。
**第二個參數指明要刷新的字節數。
*關閉文件映射對象：用CloseHandle函數
*文件對象：用CloseHandle函數

**參見**
存儲器映射輸入輸出

**參考文獻==