[轉自:http://www.cnblogs.com/fangyukuan/archive/2010/09/09/1822216.html]
Windows提供了3種進行內存管理的方法:
• 虛擬內存,最適合用來管理大型對象或結構數組。
• 內存映射文件,最適合用來管理大型數據流(通常來自文件)以及在單個計算機上運行的多個進程之間共享數據。
• 內存堆棧,最適合用來管理大量的小對象。
內存映射文件
內存映射文件與數據視圖的相關性
頁文件支持的內存映射文件
內存映射文件可以用於3個不同的目的
• 系統使用內存映射文件,以便加載和執行. exe和DLL文件。這可以大大節省頁文件空間和應用程序啓動運行所需的時間。
• 可以使用內存映射文件來訪問磁盤上的數據文件。這使你可以不必對文件執行I/O操作,並且可以不必對文件內容進行緩存。
• 可以使用內存映射文件,使同一臺計算機上運行的多個進程能夠相互之間共享數據。Windows確實提供了其他一些方法,以便在進程之間進行數據通信,但是這些方法都是使用內存映射文件來實現的,這使得內存映射文件成爲單個計算機上的多個進程互相進行通信的最有效的方法。
使用內存映射數據文件
若要使用內存映射文件,必須執行下列操作步驟:
1) 創建或打開一個文件內核對象,該對象用於標識磁盤上你想用作內存映射文件的文件。
2) 創建一個文件映射內核對象,告訴系統該文件的大小和你打算如何訪問該文件。
3) 讓系統將文件映射對象的全部或一部分映射到你的進程地址空間中。
當完成對內存映射文件的使用時,必須執行下面這些步驟將它清除:
1) 告訴系統從你的進程的地址空間中撤消文件映射內核對象的映像。
2) 關閉文件映射內核對象。
3) 關閉文件內核對象。
下面將詳細介紹這些操作步驟。
步驟1:創建或打開文件內核對象
HANDLE CreateFile(
PCSTR pszFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
PSECURITY_ATTRIBUTES psa,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
dwDesiredAccess的值
值 |
含義 |
0 |
不能讀取或寫入文件的內容。當只想獲得文件的屬性時,請設定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| |
打開文件的其他嘗試將會取得成功 |
步驟2:創建一個文件映射內核對象
調用CreateFileMapping函數告訴系統,文件映射對象需要多少物理存儲器。
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
第一個參數:hFile用於標識你想要映射到進程地址空間中的文件句柄。該句柄由前面調用的CreateFile函數返回。
第二個參數:psa參數是指向文件映射內核對象的SECURITY_ATTRIBUTES結構的指針,通常傳遞的值是NULL(它提供默認的安全特性,返回的句柄是不能繼承的)。
第三個參數:fdwProtect參數使你能夠設定這些保護屬性。大多數情況下,可以設定下表列出的3個保護屬性之一。
使用fdwProtect 參數設定的部分保護屬性
保護屬性 |
含義 |
PAGE_READONLY |
當文件映射對象被映射時,可以讀取文件的數據。必須已經將GENERIC_READ傳遞給CreateFile函數 |
PAGE_READWRITE |
當文件映射對象被映射時,可以讀取和寫入文件的數據。必須已經將GENERIC_READ | GENERIC_WRITE傳遞給Creat eFile |
PAGE_WRITECOPY |
當文件映射對象被映射時,可以讀取和寫入文件的數據。如果寫入數據,會導致頁面的私有拷貝得以創建。必須已經將GENERIC_READ或GENERIC_WRITE傳遞給CreateFile |
除了上面的頁面保護屬性外,還有4個節保護屬性
節的第一個保護屬性是SEC_NOCACHE,它告訴系統,沒有將文件的任何內存映射頁面放入高速緩存。因此,當將數據寫入該文件時,系統將更加經常地更新磁盤上的文件數據。供設備驅動程序開發人員使用的,應用程序通常不使用。
節的第二個保護屬性是SEC_IMAGE,它告訴系統,你映射的文件是個可移植的可執行(PE)文件映像。當系統將該文件映射到你的進程的地址空間中時,系統要查看文件的內容,以確定將哪些保護屬性賦予文件映像的各個頁面。例如, PE文件的代碼節( . text)通常用PAGE_ EXECUTE_READ屬性進行映射,而PE文件的數據節( .data) 則通常用PAGE_READW RITE屬性進行映射。如果設定的屬性是S E C _ I M A G E,則告訴系統進行文件映像的映射,並設置相應的頁面保護屬性。
最後兩個保護屬性是SEC_RESERVE和SEC_COMMIT,它們是兩個互斥屬性。只有當創建由系統的頁文件支持的文件映射對象時,這兩個標誌纔有意義。SEC_COMMIT標誌能使CreateFileMapping從系統的頁文件中提交存儲器。如果兩個標誌都不設定,其結果也一樣。
第四和五個參數:dwMaximumSizeHigh和dwMaximumSizeLow這兩個參數將告訴系統該文件的最大字節數
最後一個參數是pszName:它是個以0結尾的字符串,用於給該文件映射對象賦予一個名字。該名字用於與其他進程共享文件映射對象。
步驟3:將文件數據映射到進程的地址空間
將文件的數據作爲映射到該區域的物理存儲器進行提交。
PVOID MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
第一個參數:hFileMappingObject用於標識文件映射對象的句柄,該句柄是前面調用CreateFileMapping或OpenFileMapping函數返回的。
第二個參數:dwDesiredAccess用於標識如何訪問該數據。可以設定下表所列的4個值中的一個。
值 |
含義 |
FILE_MAP_WRITE |
可以讀取和寫入文件數據。CreateFileMapping函數必須通過傳遞PAGE_READWRITE標誌來調用 |
FILE_MAP_READ |
可以讀取文件數據。CreateFileMapping函數可以通過傳遞下列任何一個保護屬性來調用:PAGE_READONLY、PAGE_ READWRITE或PAGE_WRITECOPY |
FILE_MAP_ALL_ACCES S |
與FILE_MAP_WRITE相同 |
FILE_MAP_COPY |
可以讀取和寫入文件數據。如果寫入文件數據,可以創建一個頁面的私有拷貝。在Windows 2000中,CreateileMapping函數可以用PAGE_READONLY、PAGE_READWRITE或PAGE_WRITECOPY等保護屬性中的任何一個來調用。在Windows 98中,CreateFileMapping必須用PAGE_WRITECOPY來調用 |
(一個文件映射到你的進程的地址空間中時,你不必一次性地映射整個文件。相反,可以只將文件的一小部分映射到地址空間。被映射到進程的地址空間的這部分文件稱爲一個視圖。)
第三四個參數:dwFileOfsetHigh和dwFileOfsetLow參數。指定哪個字節應該作爲視圖中的第一個字節來映射。
第五個參數:dwNumberOfBytesToMap有多少字節要映射到地址空間。如果設定的值是0,那麼系統將設法把從文件中的指定位移開始到整個文件的結尾的視圖映射到地址空間。
步驟4:從進程的地址空間中撤消文件數據的映像
當不再需要保留映射到進程地址空間區域中的文件數據時,可以通過調用下面的函數將它釋放:
BOOL UnmapViewOfFile(PVOID pvBaseAddress);
參數:pvBaseAddress由MapViewOfFile函數返回。
注意:如果沒有調用這個函數,那麼在進程終止運行前,保留的區域就不會被釋放。每當調用MapViewOfFile時,系統總是在你的進程地址空間中保留一個新區域,而以前保留的所有區域將不被釋放。
爲了提高速度,系統將文件的數據頁面進行高速緩存,並且在對文件的映射視圖進行操作時不立即更新文件的磁盤映像。如果需要確保你的更新被寫入磁盤,可以強制系統將修改過的數據的一部分或全部重新寫入磁盤映像中,方法是調用FlushViewOfFile函數:
BOOL FlushViewOfFile(
PVOID pvAddress,
SIZE_T dwNumberOfBytesToFlush);
第一個參數是包含在內存映射文件中的視圖的一個字節的地址。該函數將你在這裏傳遞的地址圓整爲一個頁面邊界值。
第二個參數用於指明你想要刷新的字節數。系統將把這個數字向上圓整,使得字節總數是頁面的整數。如果你調用FlushViewOfFile函數並且不修改任何數據,那麼該函數只是返回,而不將任何信息寫入磁盤。
步驟5和步驟6:關閉文件映射對象和文件對象
用CloseHandle函數關閉相應的對象。
在代碼開始運行時關閉這些對象:
HANDLE hFile = CreateFile(...);
HANDLE hFileMapping = CreateFileMapping(hFile, ...);
CloseHandle(hFile);
PVOID pvFile = MapViewOfFile(hFileMapping, ...);
CloseHandle(hFileMapping);
// Use the memory-mapped file.
UnmapViewOfFile(pvFile);
例子:(vs2008代碼下載)
// ------------------------------------------------------------
// 文件名 : 17_FileMapping2.cpp
// 創建者 : 方煜寬
// 郵箱 : [email protected]
// 創建時間 : 2010-7-12 23:50
// 功能描述 : 內存映射數據文件
//
// ------------------------------------------------------------
#include "stdafx.h"
#include "windows.h"
#include <iostream>
using namespace std;
int _tmain(int argc,_TCHAR* argv[])
{
// Open the file that we want to map.
// 注意請在c盤,自己創建一個kuan.txt文件,並寫入內容
HANDLE hFile = ::CreateFile(L"C:\\kuan.txt",
GENERIC_READ |GENERIC_WRITE,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
// Create a file-mapping object for the file.
HANDLE hFileMapping = ::CreateFileMapping(hFile,
NULL,
PAGE_WRITECOPY,
0, 0,
NULL);
PBYTE pbFile = (PBYTE)::MapViewOfFile(hFileMapping,FILE_MAP_COPY, 0, 0, 0);
cout << pbFile << endl;
::UnmapViewOfFile(pbFile);
::CloseHandle(hFileMapping);
::CloseHandle(hFile);
return 0;
}