記憶體對映檔案

主記憶體對映檔案(Memory-mapped file),或稱「檔案對映」、「對映檔案」,是一段虛擬記憶體逐位元組對應於一個檔案或類檔案的資源,使得應用程式處理對映部分如同訪問隨機存取記憶體

Memory-mapped file」的各地常用名稱
中國大陸內存映射文件
臺灣記憶體對映檔案

得益

編輯

主要用處是增加I/O效能,特別是用於大檔案。對於小檔案,主記憶體對映檔案會導致碎片空間浪費,[1]因為主記憶體對映總是要對齊頁邊界,最少耗費4 KiB。故一個5 KiB的檔案將會對映佔用8 KiB主記憶體,浪費了3 KiB主記憶體。訪問主記憶體對映檔案比直接檔案讀寫要快幾個數量級。

主記憶體對映檔案可以只載入一部分內容到用戶的邏輯主記憶體空間。這對非常大的檔案特別有用。

使用主記憶體對映檔案可以避免顛簸英語thrashing (computer science):把相當大的檔案直接載入到主記憶體時,由於可用主記憶體不足,使得一邊讀取檔案主記憶體,同時把部分已經載入的檔案從主記憶體寫入硬碟虛存檔案中。

主記憶體對映檔案由作業系統的主記憶體管理程式負責,因此繞過了硬碟虛存的分頁檔案(page file)。[2]

分類

編輯

有兩類主記憶體對映檔案:

Persisted

編輯

Persisted檔案與硬碟檔案相關聯,當關閉主記憶體對映時,數據被寫入對應的硬碟檔案中。適合於很大的檔案。[3]

Non-persisted

編輯

Non-persisted檔案並不關聯於硬碟檔案。當關閉主記憶體對映檔案,所有數據被拋棄。適用於建立行程間通訊共用主記憶體[3]

對於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 WindowsUnix-like系統)用於載入行程[4]

另一個用途是多個行程的共用主記憶體。

第三個用途是對大檔案的讀寫。

支援的平台

編輯

一些可移植的庫實現:

Java語言FileChannel.

D語言的標準庫(std.mmfile module)[9]

Ruby語言的gem(庫)Mmap.

Python語言mmap標準庫模組.[10]

PerlSys::Mmap[11]File::Map.[12]

Microsoft .NET的P/Invoke,或者Managed access(參見 Memory-Mapped Files頁面存檔備份,存於互聯網檔案館)). 或第三方庫API.[13]

PHP的庫函數file_get_contents()( revision log頁面存檔備份,存於互聯網檔案館)).

R語言的一個庫bigmemory頁面存檔備份,存於互聯網檔案館)使用了Boost庫的實現.

J語言至少自從2005年開始支援主記憶體對映檔案。它包括了對盒裝的陣列數據和單一資料類型檔案的支援。支援可以從'data/jmf'載入。J的Jdb和JD資料庫引擎使用主記憶體對映檔案用於列儲存。

類Unix

編輯

POSIX函數mmap英語mmap()[14],建立一個主記憶體對映檔案,需要提供檔案描述子、開始位置的檔案指標、對映長度等參數[15]。 or OpenVMS

Windows

編輯

Windows API提供了一組函數以實現主記憶體對映檔案[16]

  • 建立或打開檔案內核對象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函數

參見

編輯

參考文獻

編輯
  1. ^ 存档副本. [2017-03-28]. (原始內容存檔於2011-08-07). 
  2. ^ , "What Do Memory-Mapped Files Have to Offer?".. [2017-03-28]. (原始內容存檔於2019-01-08). 
  3. ^ 3.0 3.1 Memory-Mapped Files. Microsoft Developer Network. [4 January 2016]. (原始內容存檔於2017-03-04). 
  4. ^ "Demand Paging". [2017-03-28]. (原始內容存檔於2016-03-06). 
  5. ^ Sharing memory between processes: Memory Mapped Files. Boost.org. 
  6. ^ Memory-Mapped Files. Boost.org. 
  7. ^ Memory Mapped Files for Windows and POSIX systems. SourceForge. [2017-03-28]. (原始內容存檔於2016-03-12). 
  8. ^ cpp-mmf. GitHub. [2017-03-28]. (原始內容存檔於2020-11-25). 
  9. ^ std.mmfile - D Programming Language. Digital Mars. [4 December 2011]. (原始內容存檔於2020-10-03). 
  10. ^ New Modules in 1.6. [23 December 2008]. (原始內容存檔於2006年12月30日). 
  11. ^ Sys::Mmap Perl Module. 
  12. ^ File::Map Perl Module. [2017-03-28]. (原始內容存檔於2013-06-13). 
  13. ^ DotNet. [2017-03-28]. (原始內容存檔於2010-04-19). 
  14. ^ Memory Mapped Files. [2017-03-28]. (原始內容存檔於2007-02-09). 
  15. ^ Apple - Mac OS X Leopard - Technology - UNIX. [2017-03-28]. (原始內容存檔於2009-04-23). 
  16. ^ CreateFileMapping Function (Windows). [2017-03-28]. (原始內容存檔於2008-10-10).