Windows上的檔案對映與記憶體共享
一、概念
檔案對映:將磁碟上的檔案對映入記憶體,所謂對映就是先在記憶體中分配一塊記憶體預備使用,在使用的時候再將檔案載入入記憶體。
記憶體共享:在程序A中開闢一塊記憶體,然後將這塊記憶體對映到程序B中(程序B實際也會開闢一塊記憶體空間),程序A與B訪問它們物理上各自開闢的空間,但是邏輯上是訪問同一塊記憶體。
二、檔案對映
先使用CreateFile或OpenFile開啟檔案,獲得檔案控制代碼,使用CreateFileMapping建立一個檔案對映物件並獲得該物件的控制代碼,使用檔案對映物件的控制代碼呼叫MapViewOfFileEx建立檔案對映檢視並開闢一塊記憶體空間供檔案對映使用(也可以指定分配哪塊記憶體,最後一個引數指定地址即可,也可以為NULL,這樣是作業系統選定分配的地址)。
PS:指定的lpBaseAddress必須是系統記憶體分配粒度的倍數,否則函式會失敗。
使用到的函式,如下
HANDLE CreateFileMappingA( [in] HANDLE hFile,//開啟的檔案控制代碼 [in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//NULL [in] DWORD flProtect,//許可權 [in] DWORD dwMaximumSizeHigh,//0 [in] DWORD dwMaximumSizeLow,//buffer大小 [in, optional] LPCSTR lpName //對映物件的名字 ); LPVOID MapViewOfFileEx( [in] HANDLE hFileMappingObject, [in] DWORD dwDesiredAccess,//許可權 [in] DWORD dwFileOffsetHigh, //0 [in] DWORD dwFileOffsetLow, //0 [in] SIZE_T dwNumberOfBytesToMap,//buffer大小 [in, optional] LPVOID lpBaseAddress //指定在地址處分配空間 ); BOOL UnmapViewOfFile( [in] LPCVOID lpBaseAddress );
示例:
int _tmain(void) { HANDLE hMapFile; LPCTSTR pBuf; DWORD size; hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE | SEC_COMMIT | SEC_LARGE_PAGES, 0, // max. object size size, // buffer size szName); // name of mapping object pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object FILE_MAP_ALL_ACCESS | FILE_MAP_LARGE_PAGES, // read/write permission 0, 0, BUF_SIZE); UnmapViewOfFile(pBuf); CloseHandle(hMapFile); }
我們使用MapViewOfFileEx返回的地址進行讀寫對映進來的檔案,在修改完畢後作業系統不會馬上寫回磁碟上的檔案,呼叫UnmapViewOfFile後會寫回,但是同時會銷燬檢視,所以我們可以使用FlushViewOfFile來進行及時的修改返回。
BOOL FlushViewOfFile( [in] LPCVOID lpBaseAddress, [in] SIZE_T dwNumberOfBytesToFlush );
PS:
通過對映檢視修改檔案時,最後修改時間戳可能不會自動更新。
檔案的對映檢視不能保證與 ReadFile或 WriteFile函式正在訪問的檔案一致。
MapViewOfFileEx可以處理遠端檔案,但它並不能保持它們的連貫性。
三、記憶體共享
當CreateFileMapping中的檔案控制代碼為-1時,則建立的對映物件用於共享記憶體。
一個程序可以使用 DuplicateHandle函式將檔案對映物件控制代碼複製到另一個程序中,或者另一個程序可以使用OpenFileMapping函式按名稱開啟檔案對映物件 。
另一端的程序同樣也需使用MapViewOfFileEx構建檢視,其他的步驟與檔案對映相同。
HANDLE OpenFileMappingW( [in] DWORD dwDesiredAccess,//許可權 [in] BOOL bInheritHandle, //false [in] LPCWSTR lpName //物件名字,與Create的相同 );
要獲取檢視的大小,請使用 VirtualQueryEx函式。
SIZE_T VirtualQueryEx( [in] HANDLE hProcess, [in, optional] LPCVOID lpAddress, [out] PMEMORY_BASIC_INFORMATION lpBuffer, [in] SIZE_T dwLength );
lpAddress指向要查詢的頁面區域的基地址的指標。
lpBuffer指向 MEMORY_BASIC_INFORMATION結構的指標,在該結構中返回有關指定頁面範圍的資訊。