win32下進程間通信(共享內(nèi)存)實例分析
一、概述
很多情況下在Windows程序中,各個進程之間往往需要交換數(shù)據(jù),進行數(shù)據(jù)通訊。WIN32 API提供了許多函數(shù)使我們能夠方便高效的進行進程間的通訊,通過這些函數(shù)我們可以控制不同進程間的數(shù)據(jù)交換。
進程間通訊(即:同機通訊)和數(shù)據(jù)交換有多種方式:消息、共享內(nèi)存、匿名(命名)管道、郵槽、Windows套接字等多種技術(shù)?!肮蚕韮?nèi)存”(shared memory)可以定義為對一個以上的進程是可見的內(nèi)存或存在于多個進程的虛擬地址空間。例如:如果兩個進程使用相同的DLL,只把DLL的代碼頁裝入內(nèi)存一次,其他所有映射這個DLL的進程只要共享這些代碼頁就可以了;利用消息機制實現(xiàn)IPC雖然有交換的數(shù)據(jù)量小、攜帶的信息少等缺點,但由于其實現(xiàn)方便、應(yīng)用靈活而廣泛應(yīng)用于無須大量、頻繁數(shù)據(jù)交換的內(nèi)部進程通訊系統(tǒng)之中。
二、同機進程間共享內(nèi)存的實現(xiàn)
采用內(nèi)存映射文件實現(xiàn)WIN32進程間的通訊:Windows中的內(nèi)存映射文件的機制為我們高效地操作文件提供了一種途徑,它允許我們在WIN32進程中保留一段內(nèi)存區(qū)域,把硬盤或頁文件上的目標(biāo)文件映射到這段虛擬內(nèi)存中。注意:在程序?qū)崿F(xiàn)中必須考慮各進程之間的同步問題。
具體實現(xiàn)步驟如下:
1、在服務(wù)器端進程中調(diào)用內(nèi)存映射API函數(shù)CreateFileMapping創(chuàng)建一個有名字標(biāo)識的共享內(nèi)存;
函數(shù)CreateFileMapping原型如下:
HANDLE CreateFileMapping ( HANDLE hFile, // 映射文件的句柄,若設(shè)為0xFFFFFFFF(即:INVALID_HANDLE_VALUE)則創(chuàng)建一個進程間共享的對象 LPSECURITY_ATTRIBUTES lpFileMappingAttributes, //安全屬性 DWORD flProtect, //保護方式 DWORD dwMaximumSizeHigh, //對象的大小 DWORD dwMaximumSizeLow, LPCTSTR lpName // 映射文件名,即共享內(nèi)存的名稱 );
與虛擬內(nèi)存類似,保護方式參數(shù)可以是PAGE_READONLY或是PAGE_READWRITE。如果多進程都對同一共享內(nèi)存進行寫訪問,則必須保持相互間同步。映射文件還可以指定PAGE_WRITECOPY標(biāo)志,可以保證其原始數(shù)據(jù)不會遭到破壞,同時允許其他進程在必要時自由的操作數(shù)據(jù)的拷貝。
例如:創(chuàng)建一個名為“zzj”的長度為4096字節(jié)的有名映射文件:
HANDLE m_hMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000," zzj");
2、在創(chuàng)建文件映射對象后,服務(wù)器端進程調(diào)用MapViewOfFile函數(shù)映射到本進程的地址空間內(nèi);
例:映射緩存區(qū)視圖
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
3、客戶端進程訪問共享內(nèi)存對象,需要通過內(nèi)存對象名調(diào)用OpenFileMapping函數(shù),以獲得共享內(nèi)存對象的句柄
HANDLE m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,FALSE," zzj");
4、如果客戶端進程獲得共享內(nèi)存對象的句柄成功,則調(diào)用MapViewOfFile函數(shù)來映射對象視圖。用戶可以使用該對象視圖來進行數(shù)據(jù)讀寫操作,以達(dá)到數(shù)據(jù)通訊的目的。
例:映射緩存區(qū)視圖
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
5、當(dāng)用戶進程結(jié)束使用共享內(nèi)存后,調(diào)用UnmapViewOfFile函數(shù)以取消其地址空間內(nèi)的視圖:
if (m_pBaseMapFile) { UnmapViewOfFile(m_pBaseMapFile); SharedMapView=NULL; }
三、使用文件映射實現(xiàn)共享內(nèi)存。
FileMapping用于將存在于磁盤的文件放進一個進程的虛擬地址空間,并在該進程的虛擬地址空間中產(chǎn)生一個區(qū)域用于“存放”該文件,這個空間就叫做File View(存放在進程的虛擬內(nèi)存中),系統(tǒng)并同時產(chǎn)生一個File Mapping Object(存放于物理內(nèi)存中)用于維持這種映射關(guān)系,這樣當(dāng)多個進程需要讀寫那個文件的數(shù)據(jù)時,它們的File View其實對應(yīng)的都是同一個File Mapping Object,這樣做可節(jié)省內(nèi)存和保持?jǐn)?shù)據(jù)的同步性,并達(dá)到數(shù)據(jù)共享的目的。
當(dāng)然在一個應(yīng)用向文件中寫入數(shù)據(jù)時,其它進程不應(yīng)該去讀取這個正在寫入的數(shù)據(jù)。這就需要進行一些同步的操作。下邊來看一下具體的API。
CreateFileMaping的用法:
HANDLE CreateFileMapping( //返回FileMapping Object的句柄 HANDLE hFile, //想要產(chǎn)生映射的文件的句柄 LPSECURITY_ATTRIBUTES lpAttributes, //安全屬性(只對NT和2000生效) DWORD flProtect, //保護標(biāo)致 DWORD dwMaximumSizeHigh, //在DWORD的高位中存放 File Mapping Object //的大小 DWORD dwMaximumSizeLow, //在DWORD的低位中存放 File Mapping Object //的大?。ㄍǔ_@兩個參數(shù)有一個為0) LPCTSTR lpName //File Mapping Object的名稱。 );
1)物理文件句柄
任何可以獲得的物理文件句柄,如果你需要創(chuàng)建一個物理文件無關(guān)的內(nèi)存映射也無妨,將它設(shè)置成為 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了.
如果需要和物理文件關(guān)聯(lián),要確保你的物理文件創(chuàng)建的時候的訪問模式和"保護設(shè)置"匹配,比如:物理文件只讀,內(nèi)存映射需要讀寫就會發(fā)生錯誤。推薦你的物理文件使用獨占方式創(chuàng)建。
如果使用 INVALID_HANDLE_VALUE,也需要設(shè)置需要申請的內(nèi)存空間的大小,無論物理文件句柄參數(shù)是否有效,這樣 CreateFileMapping就可以創(chuàng)建一個和物理文件大小無關(guān)的內(nèi)存空間給你,甚至超過實際文件大小,如果你的物理文件有效,而大小參數(shù)為0,則返回給你的是一個和物理文件大小一樣的內(nèi)存空間地址范圍。返回給你的文件映射地址空間是可以通過復(fù)制,集成或者命名得到,初始內(nèi)容為0。
2)保護設(shè)置
就是安全設(shè)置,不過一般設(shè)置NULL就可以了,使用默認(rèn)的安全配置. 在win2k下如果需要進行限制,這是針對那些將內(nèi)存文件映射共享給整個網(wǎng)絡(luò)上面的應(yīng)用進程使用是,可以考慮進行限制.
3)高位文件大小
32位地址空間,設(shè)置為0。
4) 共享內(nèi)存名稱
命名可以包含 "Global"或者 "Local" 前綴在全局或者會話名空間初級文件映射.其他部分可以包含任何除了()以外的字符,可以參考 Kernel Object Name Spaces.
5)調(diào)用CreateFileMapping的時候GetLastError的對應(yīng)錯誤
ERROR_FILE_INVALID 如果企圖創(chuàng)建一個零長度的文件映射,應(yīng)有此報
ERROR_INVALID_HANDLE 如果發(fā)現(xiàn)你的命名內(nèi)存空間和現(xiàn)有的內(nèi)存映射,互斥量,信號量,臨界區(qū)同名就麻煩了
ERROR_ALREADY_EXISTS 表示內(nèi)存空間命名已經(jīng)存在
使用函數(shù)CreateFileMapping創(chuàng)建一個想共享的文件數(shù)據(jù)句柄,然后使用MapViewOfFile來獲取共享的內(nèi)存地址,然后使用OpenFileMapping函數(shù)在另一個進程里打開共享文件的名稱,這樣就可以實現(xiàn)不同的進程共享數(shù)據(jù)。
代碼示例:這個程序包括一個客戶端和一個服務(wù)端,服務(wù)端創(chuàng)建共享內(nèi)存,客戶端打開共享內(nèi)存,兩者通過兩個事件互斥訪問共享內(nèi)存,實現(xiàn)一個小功能,就是服務(wù)端進程從控制臺讀入數(shù)據(jù)發(fā)送給客戶端進程。
服務(wù)端:
#include "stdafx.h" #include <Windows.h> #include <iostream> using namespace std; int main() { HANDLE hMutex = NULL; HANDLE hFileMapping = NULL; LPVOID lpShareMemory = NULL; HANDLE hServerWriteOver = NULL; HANDLE hClientReadOver = NULL; //create share memory hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024*1024, L"ShareMemoryTest"); if (NULL == hFileMapping) { cout << "CreateFileMapping fail:" << GetLastError() << endl; goto SERVER_SHARE_MEMORY_END; } lpShareMemory = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, //memory start address 0); //all memory space if (NULL == lpShareMemory) { cout << "MapViewOfFile" << GetLastError() << endl; goto SERVER_SHARE_MEMORY_END; } hMutex = CreateMutex(NULL, FALSE, L"SM_Mutex"); if (NULL == hMutex || ERROR_ALREADY_EXISTS == GetLastError()) { cout << "CreateMutex" << GetLastError() << endl; goto SERVER_SHARE_MEMORY_END; }//多個線程互斥訪問 //send data hServerWriteOver = CreateEvent(NULL, TRUE, FALSE, L"ServerWriteOver"); hClientReadOver = CreateEvent(NULL, TRUE, FALSE, L"ClientReadOver"); if (NULL == hServerWriteOver || NULL == hClientReadOver) { cout << "CreateEvent" << GetLastError() << endl; goto SERVER_SHARE_MEMORY_END; } char p = 0; char* q = (char*)lpShareMemory; do { p = getchar(); if (WaitForSingleObject(hClientReadOver, 5*1000) != WAIT_OBJECT_0) goto SERVER_SHARE_MEMORY_END; q[0] = p; if (!ResetEvent(hClientReadOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件對象設(shè)置為無信號狀態(tài) if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件對象設(shè)置為有信號狀態(tài) } while (p != '\n'); SERVER_SHARE_MEMORY_END: //release share memory if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver); if (NULL != hClientReadOver) CloseHandle(hClientReadOver); if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory); if (NULL != hFileMapping) CloseHandle(hFileMapping); if (NULL != hMutex) ReleaseMutex(hMutex); return 0; }
客戶端:
#include "stdafx.h" #include <Windows.h> #include <iostream> using namespace std; int main() { HANDLE hMutex = NULL; HANDLE hFileMapping = NULL; LPVOID lpShareMemory = NULL; HANDLE hServerWriteOver = NULL; HANDLE hClientReadOver = NULL; hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"SM_Mutex"); if (NULL == hMutex) { if (ERROR_FILE_NOT_FOUND == GetLastError()) { cout << "OpenMutex fail: file not found!" << endl; goto CLIENT_SHARE_MEMORY_END; } else { cout << "OpenMutex fail:" << GetLastError() << endl; goto CLIENT_SHARE_MEMORY_END; } } if (WaitForSingleObject(hMutex, 5000) != WAIT_OBJECT_0)//hMutex 一旦互斥對象處于有信號狀態(tài),則該函數(shù)返回 { DWORD dwErr = GetLastError(); goto CLIENT_SHARE_MEMORY_END; } //open share memory hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"ShareMemoryTest"); if (NULL == hFileMapping) { cout << "OpenFileMapping" << GetLastError() << endl; goto CLIENT_SHARE_MEMORY_END; } lpShareMemory = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (NULL == lpShareMemory) { cout << "MapViewOfFile" << GetLastError() << endl; goto CLIENT_SHARE_MEMORY_END; } //read and write data hServerWriteOver = CreateEvent(NULL, TRUE, FALSE, L"ServerWriteOver"); hClientReadOver = CreateEvent(NULL, TRUE, FALSE, L"ClientReadOver"); if (NULL == hServerWriteOver || NULL == hClientReadOver) { cout << "CreateEvent" << GetLastError() << endl; goto CLIENT_SHARE_MEMORY_END; } char p = 0; char* q = (char*)lpShareMemory; do { if (!SetEvent(hClientReadOver)) goto CLIENT_SHARE_MEMORY_END; if (WaitForSingleObject(hServerWriteOver, INFINITE) != WAIT_OBJECT_0) goto CLIENT_SHARE_MEMORY_END; p = q[0]; putchar(p); if (!ResetEvent(hServerWriteOver)) goto CLIENT_SHARE_MEMORY_END; } while (p != '\n'); CLIENT_SHARE_MEMORY_END: //release share memory if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver); if (NULL != hClientReadOver) CloseHandle(hClientReadOver); if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory); if (NULL != hFileMapping) CloseHandle(hFileMapping); if (NULL != hMutex) ReleaseMutex(hMutex); return 0; }
上一篇:C++實現(xiàn)的一個可以寫遞歸lambda的Y函數(shù)
欄 目:C語言
下一篇:MFC命名規(guī)則匯總
本文標(biāo)題:win32下進程間通信(共享內(nèi)存)實例分析
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/3584.html
您可能感興趣的文章
- 01-10如何查看進程實際的內(nèi)存占用情況詳解
- 01-10APUE筆記之:進程環(huán)境詳解
- 01-10大數(shù)(高精度數(shù))模板(分享)
- 01-10顯示任何進程加載的DLL文件的代碼
- 01-10深入解析Linux下\r\n的問題
- 01-10進程間通信之深入消息隊列的詳解
- 01-10深入分析父子線程、進程終止順序不同產(chǎn)生的結(jié)果
- 01-10Linux C 獲取進程退出值的實現(xiàn)代碼
- 01-10解析Linux下的時間函數(shù):設(shè)置以及獲取時間的方法
- 01-10深入探討linux下進程的最大線程數(shù)、進程最大數(shù)、進程打開的文


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達(dá)式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery