轉(zhuǎn)載:原文鏈接http://www.jb51.net/article/52306.htm
這篇文章主要介紹了win32下進(jìn)程間通信(共享內(nèi)存)實例分析,對win32應(yīng)用程序及進(jìn)程的原理做了較為深入的剖析,需要的朋友可以參考下。
一蝎抽、概述
很多情況下在Windows程序中粥诫,各個進(jìn)程之間往往需要交換數(shù)據(jù),進(jìn)行數(shù)據(jù)通訊储矩。WIN32 API提供了許多函數(shù)使我們能夠方便高效的進(jìn)行進(jìn)程間的通訊,通過這些函數(shù)我們可以控制不同進(jìn)程間的數(shù)據(jù)交換。
進(jìn)程間通訊(即:同機(jī)通訊)和數(shù)據(jù)交換有多種方式:消息制跟、共享內(nèi)存铜邮、匿名(命名)管道仪召、郵槽寨蹋、Windows套接字等多種技術(shù)∪用“共享內(nèi)存”(shared memory)可以定義為對一個以上的進(jìn)程是可見的內(nèi)存或存在于多個進(jìn)程的虛擬地址空間已旧。例如:如果兩個進(jìn)程使用相同的DLL,只把DLL的代碼頁裝入內(nèi)存一次召娜,其他所有映射這個DLL的進(jìn)程只要共享這些代碼頁就可以了运褪;利用消息機(jī)制實現(xiàn)IPC雖然有交換的數(shù)據(jù)量小、攜帶的信息少等缺點玖瘸,但由于其實現(xiàn)方便秸讹、應(yīng)用靈活而廣泛應(yīng)用于無須大量、頻繁數(shù)據(jù)交換的內(nèi)部進(jìn)程通訊系統(tǒng)之中雅倒。
二璃诀、同機(jī)進(jìn)程間共享內(nèi)存的實現(xiàn)
采用內(nèi)存映射文件實現(xiàn)WIN32進(jìn)程間的通訊:Windows中的內(nèi)存映射文件的機(jī)制為我們高效地操作文件提供了一種途徑,它允許我們在WIN32進(jìn)程中保留一段內(nèi)存區(qū)域蔑匣,把硬盤或頁文件上的目標(biāo)文件映射到這段虛擬內(nèi)存中劣欢。注意:在程序?qū)崿F(xiàn)中必須考慮各進(jìn)程之間的同步問題。
具體實現(xiàn)步驟如下:
1裁良、在服務(wù)器端進(jìn)程中調(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)建一個進(jìn)程間共享的對象
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
//安全屬性
DWORD
flProtect,
//保護(hù)方式
DWORD
dwMaximumSizeHigh,
//對象的大小
DWORD
dwMaximumSizeLow,
LPCTSTR
lpName
// 映射文件名趴久,即共享內(nèi)存的名稱
);
|
與虛擬內(nèi)存類似丸相,保護(hù)方式參數(shù)可以是PAGE_READONLY或是PAGE_READWRITE。如果多進(jìn)程都對同一共享內(nèi)存進(jìn)行寫訪問彼棍,則必須保持相互間同步灭忠。映射文件還可以指定PAGE_WRITECOPY標(biāo)志,可以保證其原始數(shù)據(jù)不會遭到破壞座硕,同時允許其他進(jìn)程在必要時自由的操作數(shù)據(jù)的拷貝弛作。
例如:創(chuàng)建一個名為“zzj”的長度為4096字節(jié)的有名映射文件:
HANDLE
m_hMapFile=CreateFileMapping((``HANDLE``)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,``" zzj"``);
|
2、在創(chuàng)建文件映射對象后华匾,服務(wù)器端進(jìn)程調(diào)用MapViewOfFile函數(shù)映射到本進(jìn)程的地址空間內(nèi)映琳;
例:映射緩存區(qū)視圖
void``* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
3、客戶端進(jìn)程訪問共享內(nèi)存對象蜘拉,需要通過內(nèi)存對象名調(diào)用OpenFileMapping函數(shù)萨西,以獲得共享內(nèi)存對象的句柄
HANDLE
m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,FALSE,``" zzj"``);
|
4、如果客戶端進(jìn)程獲得共享內(nèi)存對象的句柄成功旭旭,則調(diào)用MapViewOfFile函數(shù)來映射對象視圖谎脯。用戶可以使用該對象視圖來進(jìn)行數(shù)據(jù)讀寫操作,以達(dá)到數(shù)據(jù)通訊的目的持寄。
例:映射緩存區(qū)視圖
void``* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
|
5源梭、當(dāng)用戶進(jìn)程結(jié)束使用共享內(nèi)存后娱俺,調(diào)用UnmapViewOfFile函數(shù)以取消其地址空間內(nèi)的視圖:
if
(m_pBaseMapFile)
{
UnmapViewOfFile(m_pBaseMapFile);
SharedMapView=NULL;
}
|
三、使用文件映射實現(xiàn)共享內(nèi)存废麻。
FileMapping用于將存在于磁盤的文件放進(jìn)一個進(jìn)程的虛擬地址空間荠卷,并在該進(jìn)程的虛擬地址空間中產(chǎn)生一個區(qū)域用于“存放”該文件,這個空間就叫做File View(存放在進(jìn)程的虛擬內(nèi)存中)烛愧,系統(tǒng)并同時產(chǎn)生一個File Mapping Object(存放于物理內(nèi)存中)用于維持這種映射關(guān)系油宜,這樣當(dāng)多個進(jìn)程需要讀寫那個文件的數(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ù)時社牲,其它進(jìn)程不應(yīng)該去讀取這個正在寫入的數(shù)據(jù)。這就需要進(jìn)行一些同步的操作悴了。下邊來看一下具體的API搏恤。
CreateFileMaping的用法:
HANDLE
CreateFileMapping(
//返回FileMapping Object的句柄
HANDLE
hFile,
//想要產(chǎn)生映射的文件的句柄
LPSECURITY_ATTRIBUTES lpAttributes,
//安全屬性(只對NT和2000生效)
DWORD
flProtect,
//保護(hù)標(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)建的時候的訪問模式和"保護(hù)設(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)保護(hù)設(shè)置
就是安全設(shè)置,不過一般設(shè)置NULL就可以了,使用默認(rèn)的安全配置. 在win2k下如果需要進(jìn)行限制,這是針對那些將內(nèi)存文件映射共享給整個網(wǎng)絡(luò)上面的應(yīng)用進(jìn)程使用是,可以考慮進(jìn)行限制.
3)高位文件大小
32位地址空間,設(shè)置為0。
- 共享內(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ù)在另一個進(jìn)程里打開共享文件的名稱,這樣就可以實現(xiàn)不同的進(jìn)程共享數(shù)據(jù)努潘。
代碼示例:這個程序包括一個客戶端和一個服務(wù)端诽偷,服務(wù)端創(chuàng)建共享內(nèi)存坤学,客戶端打開共享內(nèi)存,兩者通過兩個事件互斥訪問共享內(nèi)存报慕,實現(xiàn)一個小功能深浮,就是服務(wù)端進(jìn)程從控制臺讀入數(shù)據(jù)發(fā)送給客戶端進(jìn)程。
服務(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;
}