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
// 建立者:
方煜寬
// 郵箱: fangyukuan@gmail.com
// 建立時間: 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;
}
本文地址:http://www.cnblogs.com/fangyukuan/archive/2010/09/09/1822216.html