前言:我們知道windows os上有很多的api,編程語言對os進行操作,無論什么語言,最終都是通過windows api 的。
在windows編程中,有個概念是句柄,句柄指向資源(一切可以利用的物理的邏輯的資源),其中文件操作,可以將文件映射到內存,此處的文件是廣義的文件,可以指內存對象,郵件槽等。
在windows中創建一個指向文件的虛擬內存,然后多個進程創建各個進程對這塊內存的映射,通過訪問各個進程的映射內存對這塊虛擬內存進行訪問,是共享內存實現的原理。
下圖很好的標明了整個原理。
一、相關的API函數
內存映射API函數CreateFileMapping創建一個有名的共享內存:
HANDLE CreateFileMapping( HANDLE hFile, // 映射文件的句柄,設為0xFFFFFFFF以創建一個進程間共享的對象 LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安全屬性 DWORD flProtect, // 保護方式 DWORD dwMaximumSizeHigh, //對象的大小 DWORD dwMaximumSizeLow, LPCTSTR lpName // 必須為映射文件命名 );
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全設置
DWORD flProtect, //保護設置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享內存名稱
);
hFile 指定欲在其中創建映射的一個文件句柄。INVALID_HANDLE_VALUE,即0xFFFFFFFF表示在頁面文件中創建一個可共享映射文件。
lpAttributes 它指明返回的句柄是否可以被子進程所繼承,指定一個安全對象,在創建文件映射時使用。如果為NULL(用ByVal As Long傳遞零),表示使用默認安全對象
flProtect 有以下幾種方式:
PAGE_READONLY 以只讀方式打開映射
PAGE_READWRITE 以可讀、可寫方式打開映射
PAGE_WRITECOPY 為寫操作留下備份也可組合使用下述一個或多個常數:
SEC_COMMIT 為文件映射一個小節中的所有頁分配內存
SEC_IMAGE 文件是個可執行文件
SEC_RESERVE 為沒有分配實際內存的一個小節保留虛擬內存空間
dwMaximumSizeHigh 文件映射的最大長度的高32位
dwMaximumSizeLow 文件映射的最大長度的低32位。如這個參數和dwMaximumSizeHigh都是零,就用磁盤文件的實際長度。
lpName 指定文件映射對象的名字。如存在這個名字的一個映射,函數就會打開它。用vbNullString可以創建一個無名的文件映射。
LPVOID WINAPI MapViewOfFile( //將一個文件映射對象映射到當前應用程序的地址空間。
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
hFileMappingObject 為CreateFileMapping返回的文件映像對象句柄。
dwDesiredAccess 映射對象的文件數據的訪問方式,而且同樣要與CreateFileMapping()函數所設置的保護屬性相匹配。
可取以下值:
FILE_MAP_ALL_ACCESS 等價於CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ.文件映射對象被創建時必須指定PAGE_READWRITE 選項.
FILE_MAP_COPY 可以讀取和寫入文件.寫入操作會導致系統為該頁面創建一份副本.在調用 CreateFileMapping時 必須 傳入PAGE_WRITECOPY保護屬性.
FILE_MAP_EXECUTE 可以將文件中的數據在調用CreateFileMapping時可以傳入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保護屬性.
FILE_MAP_READ 可以讀取文件.在調用CreateFileMapping時可以傳入PAGE_READONLY或PAGE_READWRITE保護屬性.
FILE_MAP_WRITE 可以讀取和寫入文件.在調用CreateFileMapping時必須傳入PAGE_READWRITE保護屬性.
dwFileOffsetHigh 表示文件映射起始偏移的高32位.
dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB對齊不是必須的)
dwNumberOfBytes 指定映射文件的字節數.
三、寫端的實現
#include <iostream> #include <windows.h> #include <string.h> #include <cstring> #include <istream> using namespace std; int main() { unsigned long buff_size = 1024 * 5; PVOID pBuff=NULL; HANDLE hFile = CreateFile(L"shared_memory", //如果不映射到物理磁盤上,可以不用創建 GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile==INVALID_HANDLE_VALUE) { cout << "create file error." << endl; } HANDLE hFile_mapping = CreateFileMapping( INVALID_HANDLE_VALUE, // 如果需要創建,而不是內存對象,可以傳入hFile NULL, PAGE_READWRITE, 0, buff_size, L"shared_memory"); if (NULL != hFile_mapping) pBuff = MapViewOfFile( hFile_mapping, FILE_MAP_ALL_ACCESS, 0, 0, buff_size ); char str[1024]; while (true) { cout << "please enter data .\"exit\" end app" << endl; cin.getline(str,1024); if (str!="exit") { if (NULL != pBuff) memcpy(pBuff, str, 1024); } else if (str == "exit") { break; } } if (NULL != pBuff) FlushViewOfFile(pBuff, buff_size); cout << "finish write, wait ..." << endl; Sleep(20000); if (NULL!=pBuff) UnmapViewOfFile(pBuff); if (NULL != hFile_mapping) CloseHandle(hFile_mapping); if (NULL != hFile) CloseHandle(hFile); }
四、讀端的實現
#include <iostream> #include <windows.h> using namespace std; int main() { const unsigned long buff_size = 1024*5; LPVOID pBuff = NULL; HANDLE handle_file_mapping = OpenFileMapping( FILE_MAP_ALL_ACCESS, NULL, L"shared_memory"); if (NULL!= handle_file_mapping) pBuff = MapViewOfFile( handle_file_mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (handle_file_mapping==NULL|| pBuff==NULL) { cout << "create shared memory faild,please waitting the memory created and exect the app again!" << endl <<"enter any key to exit the app!"; string temp; cin >> temp; return 0; } string str; while (true) { cout << "enter \"yes\" receive data, or enter \"exit\" " << endl; cin >> str; if (str=="yes") { char* share_buffer = (char*)pBuff; cout << share_buffer << endl; } else if (str == "exit") { break; } } if (NULL!= pBuff) UnmapViewOfFile(pBuff); if (NULL != handle_file_mapping) CloseHandle(handle_file_mapping); return 0; }
五、結果如下