轉(zhuǎn)帖|其它|編輯:郝浩|2010-07-12 11:51:44.000|閱讀 2198 次
概述:為了進一步提高數(shù)據(jù)交換的速度,可以采用由系統(tǒng)頁文件支持的內(nèi)存映射文件而直接在內(nèi)存區(qū)域使用,顯然這種共享內(nèi)存的方式是完全可以滿足在進程間進行大數(shù)據(jù)量數(shù)據(jù)快速傳輸任務(wù)要求的。下面給出在兩個相互獨立的進程間通過文件映射對象來分配和訪問同一個共享內(nèi)存塊的應(yīng)用實例。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
共享內(nèi)存的使用
在Windows操作系統(tǒng)下,任何一個進程不允許讀取、寫入或是修改另一個進程的數(shù)據(jù)(包括變量、對象和內(nèi)存分配等),但是在某個進程內(nèi)創(chuàng)建的文件映射對象的視圖卻能夠為多個其他進程所映射,這些進程共享的是物理存儲器的同一個頁面。因此,當一個進程將數(shù)據(jù)寫入此共享文件映射對象的視圖時,其他進程可以立即獲取數(shù)據(jù)變更情況。為了進一步提高數(shù)據(jù)交換的速度,還可以采用由系統(tǒng)頁文件支持的內(nèi)存映射文件而直接在內(nèi)存區(qū)域使用,顯然這種共享內(nèi)存的方式是完全可以滿足在進程間進行大數(shù)據(jù)量數(shù)據(jù)快速傳輸任務(wù)要求的。下面給出在兩個相互獨立的進程間通過文件映射對象來分配和訪問同一個共享內(nèi)存塊的應(yīng)用實例。在本例中,由發(fā)送方程序負責向接收方程序發(fā)送數(shù)據(jù),文件映射對象由發(fā)送方創(chuàng)建和關(guān)閉,并且指定一個唯一的名字供接收程序使用。接收方程序直接通過這個唯一指定的名字打開此文件映射對象,并完成對數(shù)據(jù)的接收。
在發(fā)送方程序中,首先通過CreateFileMapping()函數(shù)創(chuàng)建一個內(nèi)存映射文件對象,如果創(chuàng)建成功則通過 MapViewOfFile()函數(shù)將此文件映射對象的視圖映射進地址空間,同時得到此映射視圖的首址。可見,共享內(nèi)存的創(chuàng)建主要是通過這兩個函數(shù)完成的。這兩個函數(shù)原形聲明如下:
HANDLE CreateFileMapping(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName); LPVOID MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap); |
CreateFileMapping()函數(shù)參數(shù)hFile指定了待映射到進程地址空間的文件句柄,如果為無效句柄則系統(tǒng)會創(chuàng)建一個使用來自頁文件而非指定磁盤文件存儲器的文件映射對象。很顯然,在本例中為了數(shù)據(jù)能快速交換,需要人為將此參數(shù)設(shè)定為INVALID_HANDLE_VALUE;參數(shù)flProtect設(shè)定了系統(tǒng)對頁面采取的保護屬性,由于需要進行讀寫操作,因此可以設(shè)置保護屬性PAGE_READWRITE;雙字型參數(shù) dwMaximumSizeHigh和dwMaximumSizeLow指定了所開辟共享內(nèi)存區(qū)的最大字節(jié)數(shù);最后的參數(shù)lpName用來給此共享內(nèi)存設(shè)定一個名字,接收程序可以通過這個名字將其打開。MapViewOfFile()函數(shù)的參數(shù)hFileMappingObject為 CreateFileMapping()返回的內(nèi)存文件映像對象句柄;參數(shù)dwDesiredAccess再次指定對其數(shù)據(jù)的訪問方式,而且需要同 CreateFileMapping()函數(shù)所設(shè)置的保護屬性相匹配。這里對保護屬性的重復(fù)設(shè)置可以確保應(yīng)用程序能更多的對數(shù)據(jù)的保護屬性進行有效控制。下面給出創(chuàng)建共享內(nèi)存的部分關(guān)鍵代
hRecvMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1000000, "DataMap"); if (hRecvMap != NULL) { lpData = (LPBYTE)MapViewOfFile(hRecvMap, FILE_MAP_WRITE, 0, 0, 0); if (lpData == NULL) { CloseHandle(hRecvMap); hRecvMap = NULL; } } // 通知接收程序內(nèi)存文件映射對象的視圖已經(jīng)打開 HWND hRecv = ::FindWindow(NULL, DECODE_PROGRAMM); if (hRecv != NULL) ::PostMessage(hRecv, WM_MAP_OPEN, 0, 0); |
數(shù)據(jù)的傳送實際是將數(shù)據(jù)從發(fā)送方寫到共享內(nèi)存中,然后由接收程序及時從中取走即可。數(shù)據(jù)從發(fā)送方程序?qū)懙焦蚕韮?nèi)存比較簡單,只需用 memcpy()函數(shù)將數(shù)據(jù)拷貝過去,關(guān)鍵在于能及時通知接收程序數(shù)據(jù)已寫入到共享內(nèi)存,并讓其即使取走。在這里仍采取消息通知的方式,當數(shù)據(jù)寫入共享內(nèi)存后通過PostMessage()函數(shù)向接收方程序發(fā)送消息,接收方在消息響應(yīng)函數(shù)中完成對數(shù)據(jù)的讀取:
// 數(shù)據(jù)復(fù)制到共享內(nèi)存 memcpy(lpData, RecvBuf, sizeof(RecvBuf)); // 通知接收方接收數(shù)據(jù) HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM); if (hDeCode != NULL) ::PostMessage(hDeCode, WM_DATA_READY, (WPARAM)0, (LPARAM)sizeof(RecvBuf)); |
當數(shù)據(jù)傳輸結(jié)束,即將退出程序時,需要將映射進來的內(nèi)存文件映射對象視圖卸載和資源的釋放等處理。這部分工作主要由 UnmapViewOfFile()和CloseHandle()等函數(shù)完成:
HWND hDeCode = ::FindWindow(NULL, DECODE_PROGRAMM); if (hDeCode != NULL) ::PostMessage(hDeCode, WM_MAP_CLOSE, 0, 0); if (lpData != NULL) { UnmapViewOfFile(lpData); lpData = NULL; } if (hRecvMap != NULL) { CloseHandle(hRecvMap); hRecvMap = NULL; } |
在接收程序中,在收到由發(fā)送放發(fā)出的WM_MAP_OPEN消息后,由OpenFileMapping()函數(shù)打開由名字"DataMap"指定的文件映射對象,如果執(zhí)行成功,繼續(xù)用MapViewOfFile()函數(shù)將此文件映射對象的視圖映射到接收應(yīng)用程序的地址空間并得到其首址:
m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "DataMap"); if (m_hReceiveMap == NULL) return; m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0); if (m_lpbReceiveBuf == NULL) { CloseHandle(m_hReceiveMap); m_hReceiveMap=NULL; } |
當發(fā)送方程序?qū)?shù)據(jù)寫入到共享內(nèi)存后,接收方將收到消息WM_DATA_READY,在響應(yīng)函數(shù)中將數(shù)據(jù)從共享內(nèi)存復(fù)制到本地緩存中,再進行后續(xù)的處理。同發(fā)送程序類似,在接收程序數(shù)據(jù)接收完畢后,也需要用UnmapViewOfFile()、CloseHandle()等函數(shù)完成對文件視圖等打開過資源的釋放:
// 從共享內(nèi)存接收數(shù)據(jù) memcpy(RecvBuf, (char*)(m_lpbReceiveBuf), (int)lParam); …… // 程序退出前資源的釋放 UnmapViewOfFile(m_lpbReceiveBuf); m_lpbReceiveBuf = NULL; CloseHandle(m_hReceiveMap); m_hReceiveMap = NULL; |
小結(jié)
經(jīng)實際測試,使用共享內(nèi)存在處理大數(shù)據(jù)量數(shù)據(jù)的快速交換時表現(xiàn)出了良好的性能,在數(shù)據(jù)可靠性等方面要遠遠高于發(fā)送WM_COPYDATA消息的方式。這種大容量、高速的數(shù)據(jù)共享處理方式在設(shè)計高速數(shù)傳通訊類軟件中有著很好的使用效果。本文所述代碼在Windows 2000下由Microsoft Visual C++ 6.0編譯通過。
//附:在dll中共享數(shù)據(jù),在宿主進程中讀取共享的數(shù)據(jù);以下程序在WinXP Pro + VC6 通過測試
//dll創(chuàng)建共享數(shù)據(jù);
// create mapping file
HANDLE hSockSrvRecMap;
LPBYTE lpData;
char DataBuf[128];
hSockSrvRecMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 1024, "SockSrvDataMap");
if (hSockSrvRecMap != NULL)
{
lpData = (LPBYTE)MapViewOfFile(hSockSrvRecMap, FILE_MAP_WRITE, 0, 0, 0);
if (lpData == NULL)
{
CloseHandle(hSockSrvRecMap);
hSockSrvRecMap = NULL;
}
}
for (int i=0; i<128; i++) {
DataBuf[i] = i;
}
memcpy(lpData,DataBuf,sizeof(DataBuf));
//宿主進程讀取共享數(shù)據(jù);
m_hReceiveMap = OpenFileMapping(FILE_MAP_READ, FALSE, "SockSrvDataMap");
if (m_hReceiveMap == NULL)
return;
m_lpbReceiveBuf = (LPBYTE)MapViewOfFile(m_hReceiveMap,FILE_MAP_READ,0,0,0);
if (m_lpbReceiveBuf == NULL)
{
CloseHandle(m_hReceiveMap);
m_hReceiveMap=NULL;
}
for (int i=0; i<18; i++) {
TRACE("%d ",*(m_lpbReceiveBuf++));
}
TRACE("\n mapping data dump complete.\n");
// 不需要內(nèi)存映射的時候要關(guān)閉,兩邊都需要關(guān)閉內(nèi)存映射文件
if (m_lpbReceiveBuf != NULL)
{
UnmapViewOfFile(m_lpbReceiveBuf);
m_lpbReceiveBuf = NULL;
}
if (m_hReceiveMap != NULL)
{
CloseHandle(m_hReceiveMap);
m_hReceiveMap = NULL;
}
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客