win32下進(jìn)程間通信(共享內(nèi)存)實例分析

轉(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。

  1. 共享內(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;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末飞苇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜗顽,更是在濱河造成了極大的恐慌布卡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雇盖,死亡現(xiàn)場離奇詭異忿等,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)崔挖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門贸街,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狸相,你說我怎么就攤上這事薛匪。” “怎么了脓鹃?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵逸尖,是天一觀的道長。 經(jīng)常有香客問我瘸右,道長娇跟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任尊浓,我火速辦了婚禮逞频,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘栋齿。我一直安慰自己苗胀,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布瓦堵。 她就那樣靜靜地躺著基协,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菇用。 梳的紋絲不亂的頭發(fā)上澜驮,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音惋鸥,去河邊找鬼杂穷。 笑死悍缠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的耐量。 我是一名探鬼主播飞蚓,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廊蜒!你這毒婦竟也來了趴拧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤山叮,失蹤者是張志新(化名)和其女友劉穎著榴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屁倔,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡脑又,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了汰现。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挂谍。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞎饲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炼绘,我是刑警寧澤嗅战,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站俺亮,受9級特大地震影響驮捍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脚曾,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一东且、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧本讥,春花似錦珊泳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撞芍,卻和暖如春秧了,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背序无。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工验毡, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留衡创,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓晶通,卻偏偏與公主長得像璃氢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子录择,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 2016年國慶假期終于把此書過完隘竭,整理筆記和體會于此塘秦。 關(guān)于書名 書名源于俄羅斯的演員斯坦尼斯拉夫斯基創(chuàng)作的《演員...
    李劍飛的簡書閱讀 7,220評論 2 65
  • 01. 清遠(yuǎn)鎮(zhèn)內(nèi)無莊稼,田地中均是成片成片數(shù)十年頭甚至上百年的柿子樹动看,如今秋日里尊剔,柿子樹葉紅勝火,果實掛滿枝頭菱皆,遠(yuǎn)...
    挺洋氣一姑娘閱讀 748評論 1 2
  • 愛情是永恒的主題须误,是男女之間情感高尚的表現(xiàn) 真正的愛情是1314式的 隨著現(xiàn)在社會的發(fā)展, 離婚了增高仇轻,網(wǎng)絡(luò)上 1...
    戰(zhàn)福剛閱讀 914評論 0 0
  • 前言 如果問一個iOS開發(fā)的同學(xué)京痢,你們的網(wǎng)絡(luò)請求是使用的什么框架呢?不用懷疑篷店,十有八九都會回答使用的是AFNetw...
    Doctor_Peng閱讀 609評論 2 2
  • 夏天夜跑 冬季游泳 想學(xué)滑雪 多喝水 擦蘆薈膠 敷面膜 畫眉毛 剃毛 皮膚保養(yǎng) 運動是一種涉及體力和技巧的由一套規(guī)...
    龍G閱讀 257評論 0 1