Windows系統程序設計之內存映射
【作者】北極星2003
【來源】看雪技術論壇(bbs.pediy.com)
【時間】2006年8月11日
相信對於大家來說,內存映射技術已經是個很熟悉的技術了。在這裏我只是作個總結,希望對那些新手朋友有幫助。
內存映射文件通常有兩個用途:一是內存映射磁盤文件,這對於大數據文件的處理比較適合;二是共享內存,作爲進程間通信的一種方式。
附件:memmap.rar
實例一:內存映射實例--文件分割器
1、 設計目標
設計文件分割器,作爲演示內存映射磁盤文件實例。
2、 設計思路
內存映射磁盤文件的一般步驟:
(1) 打開文件
(2) 創建內存映射內核對象
(3) 映射文件視圖
(4) 在內存中處理數據
(5) 卸載文件視圖
(6) 關閉內存映射內核對象
(7) 關閉文件
分割思路,把原文件按照設置的文件塊大小依次映射到內存,然後生成一個新文件,下圖中藍色箭頭所示。
還原思路,把子文件依次映射到內存,然後再回寫到原文件的各個部分,下圖中紅色箭頭所示。
3、 詳細設計
(1) 定義文件信息塊結構
文件信息塊保存着分割文件時的信息,也是還原時的必備文件。
代碼:
typedef struct _FILE_ITEM_INFO {
DWORD dwLowFileSize ; // 文件大小低字段
DWORD dwHighFileSize ; // 文件大小高字段
DWORD dwPartSize ; // 文件塊大小
DWORD dwPartNum ; // 文件塊數量
CString szFileName ; // 源文件名
} FILE_ITEM_INFO ;
typedef FILE_ITEM_INFO* PFILE_ITEM_INFO ;
(2) 文件分割實現部分
代碼:
DWORD CFD::FDFileDivide ( PFILE_ITEM_INFO pItem )
{
// 刪除該文件原有的分塊文件
this->DeleteAllPartFiles ( pItem->szFileName ) ;
// 創建信息文件
HANDLE hFile = CreateFile (
pItem->szFileName + ".INFO",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
return GetLastError() ;
// 寫入信息文件
DWORD dwWriteByte ;
WriteFile ( hFile, &(pItem->dwLowFileSize), sizeof(DWORD), &dwWriteByte, NULL ) ;
WriteFile ( hFile, &(pItem->dwPartSize), sizeof(DWORD), &dwWriteByte, NULL ) ;
WriteFile ( hFile, &(pItem->dwPartNum), sizeof(DWORD), &dwWriteByte, NULL ) ;
CloseHandle ( hFile ) ;
// 打開目標文件
hFile = CreateFile (
pItem->szFileName,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
return GetLastError() ;
// 創建文件內存映射內核對象
HANDLE hMapFile = CreateFileMapping (
hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL ) ;
if ( hMapFile == NULL )
{
CloseHandle ( hFile ) ;
return GetLastError() ;
}
CString TempStr ;
DWORD dwCurAddr = 0, dwCurPart = 0 ;
LPVOID lpMapAddr = 0 ;
// 分塊循環映射文件
for ( UINT i = 1; i <= pItem->dwPartNum; i++ )
{
dwCurPart = pItem->dwLowFileSize - dwCurAddr ;
if ( dwCurPart > pItem->dwPartSize )
dwCurPart = pItem->dwPartSize ;
lpMapAddr = MapViewOfFile ( hMapFile, FILE_MAP_READ, 0, dwCurAddr, dwCurPart ) ;
if ( lpMapAddr == NULL )
{
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
return GetLastError() ;
}
dwCurAddr += dwCurPart ;
TempStr.Format ( "%s.PART_%d", pItem->szFileName, i ) ;
DeleteFile ( TempStr ) ;
HANDLE hNewFile = CreateFile (
TempStr,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
return GetLastError() ;
HANDLE hNewMapFile = CreateFileMapping (
hNewFile,
NULL,
PAGE_READWRITE,
0,
dwCurPart,
NULL ) ;
if ( hNewMapFile == NULL )
{
CloseHandle ( hNewFile ) ;
return GetLastError() ;
}
LPVOID lpNewMapAddr = MapViewOfFile ( hNewMapFile, FILE_MAP_WRITE, 0, 0, 0 ) ;
if ( lpMapAddr == NULL )
{
CloseHandle ( hNewMapFile ) ;
CloseHandle ( hNewFile ) ;
return GetLastError() ;
}
memcpy ( lpNewMapAddr, lpMapAddr, dwCurPart ) ;
UnmapViewOfFile ( lpMapAddr ) ;
UnmapViewOfFile ( lpNewMapAddr ) ;
CloseHandle ( hNewMapFile ) ;
CloseHandle ( hNewFile ) ;
}
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
return 0 ;
}
(3) 文件還原部分
代碼:
DWORD CFD::FDFileConnect ( PFILE_ITEM_INFO pItem )
{
DeleteFile ( pItem->szFileName ) ;
// 打開目標文件
HANDLE hFile = CreateFile (
pItem->szFileName,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
return GetLastError() ;
// 創建文件內存映射內核對象
HANDLE hMapFile = CreateFileMapping (
hFile,
NULL,
PAGE_READWRITE,
0,
pItem->dwLowFileSize,
NULL ) ;
if ( hMapFile == NULL )
{
CloseHandle ( hFile ) ;
return GetLastError() ;
}
CString TempStr ;
DWORD dwCurAddr = 0, dwCurPart = 0 ;
LPVOID lpMapAddr = 0 ;
// 分塊循環映射文件
for ( UINT i = 1; i <= pItem->dwPartNum; i++ )
{
dwCurPart = pItem->dwLowFileSize - dwCurAddr ;
if ( dwCurPart > pItem->dwPartSize )
dwCurPart = pItem->dwPartSize ;
lpMapAddr = MapViewOfFile ( hMapFile, FILE_MAP_WRITE, 0, dwCurAddr, dwCurPart ) ;
if ( lpMapAddr == NULL )
{
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
return GetLastError() ;
}
dwCurAddr += dwCurPart ;
TempStr.Format ( "%s.PART_%d", pItem->szFileName, i ) ;
HANDLE hNewFile = CreateFile (
TempStr,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ) ;
if ( hFile == INVALID_HANDLE_VALUE )
{
UnmapViewOfFile ( lpMapAddr ) ;
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
return GetLastError() ;
}
HANDLE hNewMapFile = CreateFileMapping (
hNewFile,
NULL,
PAGE_READONLY,
0,
0,
NULL ) ;
if ( hNewMapFile == NULL )
{
UnmapViewOfFile ( lpMapAddr ) ;
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
CloseHandle ( hNewFile ) ;
return GetLastError() ;
}
LPVOID lpNewMapAddr = MapViewOfFile ( hNewMapFile, FILE_MAP_READ, 0, 0, 0 ) ;
if ( lpMapAddr == NULL )
{
UnmapViewOfFile ( lpMapAddr ) ;
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
CloseHandle ( hNewMapFile ) ;
CloseHandle ( hNewFile ) ;
return GetLastError() ;
}
memcpy ( lpMapAddr, lpNewMapAddr, dwCurPart ) ;
FlushViewOfFile ( lpMapAddr, dwCurPart ) ;
UnmapViewOfFile ( lpMapAddr ) ;
UnmapViewOfFile ( lpNewMapAddr ) ;
CloseHandle ( hNewMapFile ) ;
CloseHandle ( hNewFile ) ;
}
CloseHandle ( hMapFile ) ;
CloseHandle ( hFile ) ;
this->DeleteAllPartFiles ( pItem->szFileName ) ;
return 0 ;
}
(4) 性能測試
對於小文件沒有測試的必要,這裏選個較大的文件作爲測試對象。
測試平臺信息:P4 2.26GHz + WinXP sp2 + 256 MB內存
測試對象大小:203 MB
如果塊大小爲 10 MB, 分割需6秒左右,還原(合併)需7秒左右
如果塊大小爲 1 MB, 分割需13秒左右,還原(合併)需12秒左右
實例二:共享內存示例
1、 設計目標
在兩個相互獨立的進程間通過文件映射對象來分配和訪問同一個共享內存塊的應用實例。
2、 設計思路
這個例子比較簡單,從寫入端進程把信息寫到共享內存,然後讀取端進程從共享內存中讀取信息。
3、 詳細設計
(1) 創建共享內存
代碼:
void CShareMemDlg::OnSetup()
{
if ( bSetup )
{
this->MessageBox ( "已經建立共享內存文件" ) ;
return ;
}
hMapFile = CreateFileMapping (
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE | SEC_COMMIT,
0,
uShareMemSize,
"ShareMemSample" ) ;
if ( hMapFile == NULL )
{
this->MessageBox ( "創建共享內存映射文件失敗!" ) ;
return ;
}
lpBase = MapViewOfFile (
hMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,
0,
0,
0 ) ;
if ( this->lpBase == NULL )
{
this->MessageBox ( "創建共享內存映射文件失敗!" ) ;
return ;
}
this->MessageBox ( "創建內存共享成功!" ) ;
bSetup = true ;
}
(2)從共享內存中讀取信息
代碼:
void CShareMemReadDlg::OnRead()
{
HANDLE hMapFile = OpenFileMapping ( FILE_MAP_READ, FALSE, "ShareMemSample");
if ( hMapFile == NULL )
{
DWORD dwErrorCode = GetLastError () ;
this->MessageBox ( "打開共享內存映射文件失敗!" ) ;
return ;
}
LPVOID lpBase = MapViewOfFile (
hMapFile,
FILE_MAP_READ,
0,
0,
0 ) ;
if ( lpBase == NULL )
{
DWORD dwErrorCode = GetLastError () ;
this->MessageBox ( "打開共享內存映射文件失敗!" ) ;
return ;
}
char *pReadInfo = (char*)lpBase ;
UINT uLength = strlen ( pReadInfo ) ;
memcpy ( this->m_ReadInfo.GetBuffer( uLength + 1 ), lpBase, uLength + 1 ) ;
this->UpdateData ( false ) ;
UnmapViewOfFile ( lpBase ) ;
CloseHandle ( hMapFile ) ;
}
【參考文獻】
[1].Windows核心編程 Jeffrey Richter著