轉自:http://www.zxbc.cn/html/20080524/51094.html
與虛擬內存一樣,內存映射文件可以用來保留一個地址空間的區域,一旦該文件被映射,就可以訪問它,就像整個文件已經加載內存一樣。
??內存映射文件可以用于3個不同的目的:
???1. 系統使用內存映射文件,以便加載和執行exe 和dll文件,這可以大大節省頁文件空間和應用程序啟動運行所需的時間。
???2.可以使用內存映射文件來訪問磁盤上的數據文件,這使你可以不必對文件執行I/O操作,并且可以不必對文件內容進行緩存。
???3.可以使用內存映射文件,使同一臺計算機上運行的多個進程能夠相互之間共享數據,Windows確實提供了其他一些方法,以便在進程之間進行數據通信,但是這些方法都是使用內存映射文件來實現的,這使得內存映射文件成為單個計算機上多個進程互相進行同行的最有效的方法。
一.內存映射文件的函數包括
CreateFileMapping , OpenFileMapping, MapViewOfFile, UnmapViewOfFile
和
FlushViewOfFile
。
用法如下:
1
.
HANDLE CreateFileMapping(
? HANDLE????????????????? hFile,????????????? //
一個文件句柄
? LPSECURITY_ATTRIBUTE? lpAttributes,???????? //
定義內存映射文件對象是否可以被承
? DWORD?????????????????? flProtect,?????????? //
該內存映射文件的保護類型
? DWORD?????????????????? dwMaximumSizeHigh,//
內存映射文件的長度
? DWORD?????????????????? dwMaximumSizeLow, //
? LPCTSTR????????????????? lpName???????????? //
內存映射文件的名字
)
hFile??????
指定要映射的文件的句柄,如果這是一個已經打開的文件的句柄(
CreateFile
函數的返回值),那么將建立這個文件的內存映射文件,如果這個參數為
-1
,則建立共享內存。
lpAttribute?
安全屬性,一般設為
NULL
flProtect???
指定映射文件的保護類型,它的取值可以是
PAGE_READONLY
(內存頁面只讀)
或
PAGE_READWRITE
(內存頁面可讀寫)。
dwMaximumSizeHigh
和
dwMaximumSizeLow
參數組合指定了一個
64
位的內存映射文件的長度。一種簡單的方法是將這兩個參數全部設置為
0
,那么內存映射文件的大小將與磁盤文件大小一致。
2
.
HANDLE OpenFileMapping(
????? DWORD?????????????? dwDesiredAccess,??? //
指定保護類型
????? BOOL????????????????? bIsInheritHandle,???? //
返回的句柄是否可以被繼承
????? LPCSTR??????????????? lpName???????????? //
創建對象時使用的名字
????
)
如果創建的是共享內存,其他進程不能再使用
CreateFileMapping
函數去創建同名的內存映射文件對象,而要使用
OpenFileMapping
函數打開已創建好的對象。
dwDesiredAcess??
指定保護類型有
FILE_MAP_WRITE
或
FILE_MAP_READ
3
.
LPVOID? MapViewOfFile(
????? HANDLE? hFileMappingObject,??? //
前兩個函數返回的內存映射文件的句柄
????? DWORD? dwDesiredAcess,???? ?//
保護類型
FILE_MAP_READ ,FILE_MAP_WRITE
????? DWORD? dwFileOffsetHight,???? //
從文件的那個地址開始映射
????? DWORD? dwFileOffsetLow,
????? SIZE_T?? dwNumberOfBytesToMap //
要映射的字節數,為
0
則映射整個文件
)
4
.
BOOL? UnmapViewOfFile( LPCVOID? lpBaseAddress )
當不再使用內存映射文件時,可以通過
UmmapViewOfFile
函數撤銷映射并使用
CloseHandle
函數關閉內存映射文件的句柄。
5
.
BOOL? FlushViewOfFile(
?????? LPCVOID?? lpBaseAddress,? //
開始的地址
?????? SIZE_T???? dwNumberOfBytesToFlush //
數據塊的大小
?
)
?
如果修改了映射視圖中的內存,系統會在試圖撤銷映射或文件映射對象被刪除時自動將數據寫到磁盤上,但程序也可以根據需要將視圖中的數據立即寫到磁盤上。
二.使用步驟
1.?
使用
CreateFileMapping
創建一個內存映射文件內核對象,告訴操作系統內存映射文件需要的物理內存大小,這個步驟決定了內存映射文件的用途――究竟是為磁盤上的文件建立內存映射還是為多個進程共享數據建立共享內存。或者使用
OpenFileMapping
打開映射文件內核對象。
2.?
映射文件映射對象的全部或一部分到進程的地址空間,可以認為該操作是為文件中的內容分配線型地址空間,并將線型地址和文件內容對應起來,完成該操作的函數是
MapViewOfFile
。
三.使用內存映射文件讀文件的具體過程可以這樣:
(1)???????
調用
CreateFile
函數打開想要映射的文件,得到文件句柄
hFile
。
(2)???????
調用
CreateFileMapping
函數,并傳入文件句柄
hFile
,為該文件創建一個內存映射內核對象,得到內存映射文件的句柄
hMap
。
(3)???????
調用
MapViewOfFile
函數映射整個文件或一部分到進程的虛擬地址空間。該函數返回文件映射到內存后的起始地址。使用指向這個地址的指針就可以讀取文件的內容了。
(4)???????
調用
UnmapViewOfFile
函數來解除文件映射。
(5)???????
調用
CloseHandle
函數關閉文件對象,必須傳入內存映射文件句柄
hMap
(6)???????
調用
CloseHandle
函數關閉文件對象,必須傳入文件句柄
hFile
。
四.進程間共享內存:
共享內存主要是通過映射機制實現的。 Windows 下進程的地址空間是相互隔離的,但在物理上卻是重疊的。所謂的重疊是指同一塊內存區域可能被多個進程同時使用。當調用 CreateFileMapping 創建命名的內存映射文件對象時, Windows 即在物理內存中申請了一塊指定大小的內存區域,返回文件映射對象的新句柄 hMap 。為了能夠訪問這塊區域必須調 MapViewOfiFile 函數,促使 Windows 將此內存空間映射到進程的地址空間中。當在其他進程中訪問這塊區域時,則必須使用 OpenFileMapping 函數來取得對象句柄 hMap ,并調用 MapViewOfFile 函數得到此內存空間的一個映射。這樣一來,系統就把同一塊內存區域映射到了不同進程的地址空間中,從而達到共享內存的目的。
????HANDLE???hMapFile?=?NULL;
????FILEMAP*?pFileMap?=?NULL;
????g_hFileMap?=?::CreateFileMapping(INVALID_HANDLE_VALUE,
????????NULL,?
????????PAGE_READWRITE,?
????????0,?
????????sizeof(FILEMAP),?
????????g_FileMapFlag);
????if?(g_hFileMap?&&?ERROR_ALREADY_EXISTS?==?::GetLastError())
????{
????????pFileMap?=?(FILEMAP*)::MapViewOfFile(g_hFileMap,?FILE_MAP_READ,?0,?0,?0);
????????if?(pFileMap)
????????{
????????????ATLASSERT(::IsWindow(pFileMap->hForegroundWnd));
????????????::PostMessage(pFileMap->hForegroundWnd,?WM_SYSCOMMAND,?SC_RESTORE,?0);
????????????::SetForegroundWindow(pFileMap->hForegroundWnd);
????????????bRet?=?FALSE;
????????}
????????::UnmapViewOfFile(pFileMap);
????}