標籤:
享記憶體的方式原理就是將一份實體記憶體映射到不同進程各自的虛擬位址空間上,這樣每個進程都可以讀取同一份資料,從而實現進程通訊。因為是通過記憶體操作實現通訊,因此是一種最高效的資料交換方法。
共用記憶體在 Windows 中是用 FileMapping 實現的,從具體的實現方法上看主要通過以下幾步來實現:
1、調用 CreateFileMapping 建立一個記憶體檔案對應物件;
HANDLE CreateFileMapping( HANDLE hFile, // handle to file to map LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attributes DWORD flProtect, // protection for mapping object DWORD dwMaximumSizeHigh, // high-order 32 bits of object size DWORD dwMaximumSizeLow, // low-order 32 bits of object size LPCTSTR lpName // name of file-mapping object);
通過這個API函數 將建立一個記憶體對應檔的核心對象,用於對應檔到記憶體。與虛擬記憶體一樣,記憶體對應檔可以用來保留一個地址空間的地區,並將實體儲存體器提交
給該地區。它們之間的差別是,實體儲存體器來自一個已經位於磁碟上的檔案,而不是系統的頁檔案。
hFile:用於標識你想要映射到進程地址空間中的檔案控制代碼。該控制代碼可以通過調用C r e a t e F i l e函數返回。這裡,我們並不需要一個實際的檔案,所以,就不需要調用 CreateFile 建立一個檔案, hFile 這個參數可以填寫 INVALID_HANDLE_VALUE; lpFileMappingAttributes:參數是指向檔案對應核心對象的 SECURITY_ATTRIBUTES結構的指標,通常傳遞的值是 N U L L;flProtect:對記憶體對應檔的安全設定(PAGE_READONLY 以唯讀方式開啟映射;PAGE_READWRITE 以可讀、可寫方式開啟映射;PAGE_WRITECOPY 為寫操作留下備份)dwMaximumSizeHigh:檔案對應的最大長度的高32位。dwMaximumSizeLow:檔案對應的最大長度的低32位。如這個參數和dwMaximumSizeHigh都是零,就用磁碟檔案的實際長度。lpName:指定檔案對應物件的名字,別的進程就可以用這個名字去調用 OpenFileMapping 來開啟這個 FileMapping 對象。
如果建立成功,返回建立的記憶體對應檔的控制代碼,如果已經存在,則也返回其控制代碼,但是調用 GetLastError()返回的錯誤碼是:183(ERROR_ALREADY_EXISTS),如果建立失敗,則返回NULL;
2、調用 MapViewOfFile 映射到當前進程的虛擬位址上;
如果調用CreateFileMapping成功,則調用MapViewOfFile函數,將記憶體對應檔映射到進程的虛擬位址中;
LPVOID MapViewOfFile( HANDLE hFileMappingObject, // file-mapping object to map into // address space DWORD dwDesiredAccess, // access mode DWORD dwFileOffsetHigh, // high-order 32 bits of file offset DWORD dwFileOffsetLow, // low-order 32 bits of file offset DWORD dwNumberOfBytesToMap // number of bytes to map);
hFileMappingObject:CreateFileMapping()返回的檔案映像物件控點。
dwDesiredAccess: 映射對象的檔案資料的訪問方式,而且同樣要與CreateFileMapping()函數所設定的保護屬性相匹配。
dwFileOffsetHigh: 表示檔案對應起始位移的高32位.
dwFileOffsetLow: 表示檔案對應起始位移的低32位.
dwNumberOfBytesToMap :檔案中要映射的位元組數。為0表示映射整個檔案對應物件。
3、在接收進程中開啟對應的記憶體映射對象
在資料接收進程中,首先調用OpenFileMapping()函數開啟一個命名的檔案對應核心對象,得到相應的檔案對應核心物件控點hFileMapping;如果開啟成功,則調用MapViewOfFile()函數映射對象的一個視圖,將檔案對應核心對象hFileMapping映射到當前應用程式的進程地址,進行讀取操作。(當然,這裡如果用CreateFileMapping也是可以擷取對應的控制代碼)
HANDLE OpenFileMapping( DWORD dwDesiredAccess, // access mode BOOL bInheritHandle, // inherit flag LPCTSTR lpName // pointer to name of file-mapping object);
dwDesiredAccess:同MapViewOfFile函數的dwDesiredAccess參數
bInheritHandle :如這個函數返回的控制代碼能由當前進程啟動的新進程繼承,則這個參數為TRUE。
lpName :指定要開啟的檔案對應物件名稱。
4、進行記憶體對應檔的讀寫
一旦MapViewOfFile調用成功,就可以像讀寫本進程地址空間的記憶體區一樣,進行記憶體的讀寫操作了。
//讀操作:
// 開啟成功,映射對象的一個視圖,得到指向共用記憶體的指標,顯示出裡面的資料
pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pBuffer)
{
cout << ((CThostFtdcDepthMarketDataField *)pBuffer)->LastPrice << endl;
//cout << "讀取共用記憶體資料:" << ((CThostFtdcDepthMarketDataField *)pBuffer)->LastPrice << endl;
}
else
{
cout << "開啟共用資料失敗!" << endl;
}
//寫操作:
// 映射對象的一個視圖,得到指向共用記憶體的指標,設定裡面的資料
pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pBuffer)
{
memcpy((CThostFtdcDepthMarketDataField*)pBuffer, DepthMarketData, sizeof(CThostFtdcDepthMarketDataField));
//cout << "寫入共用記憶體資料:" << ((CThostFtdcDepthMarketDataField *)pBuffer)->LastPrice << endl;
}
else
{
cout << "開啟共用資料失敗!" << endl;
}
5、清理核心對象
在用完後,要取消本進程地址空間的映射,並釋放記憶體映射對象。
//取消本進程地址空間的映射; UnmapViewOfFile(pLocalMem); pLocalMem=NULL; //關閉檔案對應核心檔案 CloseHandle(hFileMapping);
Windows進程通訊 -- 共用記憶體