win32下进程间通信(共享内存)实例分析

转载:原文链接http://www.jb51.net/article/52306.htm

这篇文章主要介绍了win32下进程间通信(共享内存)实例分析,对win32应用程序及进程的原理做了较为深入的剖析,需要的朋友可以参考下。

一、概述

很多情况下在Windows程序中,各个进程之间往往需要交换数据,进行数据通讯。WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换。

进程间通讯(即:同机通讯)和数据交换有多种方式:消息、共享内存、匿名(命名)管道、邮槽、Windows套接字等多种技术。“共享内存”(shared memory)可以定义为对一个以上的进程是可见的内存或存在于多个进程的虚拟地址空间。例如:如果两个进程使用相同的DLL,只把DLL的代码页装入内存一次,其他所有映射这个DLL的进程只要共享这些代码页就可以了;利用消息机制实现IPC虽然有交换的数据量小、携带的信息少等缺点,但由于其实现方便、应用灵活而广泛应用于无须大量、频繁数据交换的内部进程通讯系统之中。

二、同机进程间共享内存的实现

采用内存映射文件实现WIN32进程间的通讯:Windows中的内存映射文件的机制为我们高效地操作文件提供了一种途径,它允许我们在WIN32进程中保留一段内存区域,把硬盘或页文件上的目标文件映射到这段虚拟内存中。注意:在程序实现中必须考虑各进程之间的同步问题。

具体实现步骤如下:

1、在服务器端进程中调用内存映射API函数CreateFileMapping创建一个有名字标识的共享内存;

函数CreateFileMapping原型如下:

?

HANDLE CreateFileMapping (

HANDLE hFile, // 映射文件的句柄,若设为0xFFFFFFFF(即:INVALID_HANDLE_VALUE)则创建一个进程间共享的对象

LPSECURITY_ATTRIBUTES lpFileMappingAttributes, //安全属性

DWORD flProtect, //保护方式

DWORD dwMaximumSizeHigh, //对象的大小

DWORD dwMaximumSizeLow,

LPCTSTR lpName // 映射文件名,即共享内存的名称

);

|

与虚拟内存类似,保护方式参数可以是PAGE_READONLY或是PAGE_READWRITE。如果多进程都对同一共享内存进行写访问,则必须保持相互间同步。映射文件还可以指定PAGE_WRITECOPY标志,可以保证其原始数据不会遭到破坏,同时允许其他进程在必要时自由的操作数据的拷贝。

例如:创建一个名为“zzj”的长度为4096字节的有名映射文件:

?

HANDLE m_hMapFile=CreateFileMapping((``HANDLE``)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,``" zzj"``);

|

2、在创建文件映射对象后,服务器端进程调用MapViewOfFile函数映射到本进程的地址空间内;
例:映射缓存区视图

?

void``* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

3、客户端进程访问共享内存对象,需要通过内存对象名调用OpenFileMapping函数,以获得共享内存对象的句柄

?

HANDLE m_hMapFile =OpenFileMapping(FILE_MAP_WRITE,FALSE,``" zzj"``);

|

4、如果客户端进程获得共享内存对象的句柄成功,则调用MapViewOfFile函数来映射对象视图。用户可以使用该对象视图来进行数据读写操作,以达到数据通讯的目的。
例:映射缓存区视图

?

void``* m_pBaseMapFile=MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

|

5、当用户进程结束使用共享内存后,调用UnmapViewOfFile函数以取消其地址空间内的视图:

?

if (m_pBaseMapFile)

{

UnmapViewOfFile(m_pBaseMapFile);

SharedMapView=NULL;

}

|

三、使用文件映射实现共享内存。

FileMapping用于将存在于磁盘的文件放进一个进程的虚拟地址空间,并在该进程的虚拟地址空间中产生一个区域用于“存放”该文件,这个空间就叫做File View(存放在进程的虚拟内存中),系统并同时产生一个File Mapping Object(存放于物理内存中)用于维持这种映射关系,这样当多个进程需要读写那个文件的数据时,它们的File View其实对应的都是同一个File Mapping Object,这样做可节省内存和保持数据的同步性,并达到数据共享的目的。

当然在一个应用向文件中写入数据时,其它进程不应该去读取这个正在写入的数据。这就需要进行一些同步的操作。下边来看一下具体的API。

CreateFileMaping的用法:

HANDLE CreateFileMapping( //返回FileMapping Object的句柄

HANDLE hFile, //想要产生映射的文件的句柄

LPSECURITY_ATTRIBUTES lpAttributes, //安全属性(只对NT和2000生效)

DWORD flProtect, //保护标致

DWORD dwMaximumSizeHigh, //在DWORD的高位中存放

File Mapping Object //的大小

DWORD dwMaximumSizeLow, //在DWORD的低位中存放

File Mapping Object //的大小(通常这两个参数有一个为0)

LPCTSTR lpName //File Mapping Object的名称。

);

|

1)物理文件句柄

任何可以获得的物理文件句柄,如果你需要创建一个物理文件无关的内存映射也无妨,将它设置成为 0xFFFFFFFF(INVALID_HANDLE_VALUE)就可以了.

如果需要和物理文件关联,要确保你的物理文件创建的时候的访问模式和"保护设置"匹配,比如:物理文件只读,内存映射需要读写就会发生错误。推荐你的物理文件使用独占方式创建。

如果使用 INVALID_HANDLE_VALUE,也需要设置需要申请的内存空间的大小,无论物理文件句柄参数是否有效,这样 CreateFileMapping就可以创建一个和物理文件大小无关的内存空间给你,甚至超过实际文件大小,如果你的物理文件有效,而大小参数为0,则返回给你的是一个和物理文件大小一样的内存空间地址范围。返回给你的文件映射地址空间是可以通过复制,集成或者命名得到,初始内容为0。

2)保护设置

就是安全设置,不过一般设置NULL就可以了,使用默认的安全配置. 在win2k下如果需要进行限制,这是针对那些将内存文件映射共享给整个网络上面的应用进程使用是,可以考虑进行限制.

3)高位文件大小

32位地址空间,设置为0。

  1. 共享内存名称

命名可以包含 "Global"或者 "Local" 前缀在全局或者会话名空间初级文件映射.其他部分可以包含任何除了()以外的字符,可以参考 Kernel Object Name Spaces.

5)调用CreateFileMapping的时候GetLastError的对应错误

ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射,应有此报
ERROR_INVALID_HANDLE 如果发现你的命名内存空间和现有的内存映射,互斥量,信号量,临界区同名就麻烦了
ERROR_ALREADY_EXISTS 表示内存空间命名已经存在

使用函数CreateFileMapping创建一个想共享的文件数据句柄,然后使用MapViewOfFile来获取共享的内存地址,然后使用OpenFileMapping函数在另一个进程里打开共享文件的名称,这样就可以实现不同的进程共享数据。

代码示例:这个程序包括一个客户端和一个服务端,服务端创建共享内存,客户端打开共享内存,两者通过两个事件互斥访问共享内存,实现一个小功能,就是服务端进程从控制台读入数据发送给客户端进程。

服务端:

#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;``//把指定的事件对象设置为无信号状态

if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;``//把指定的事件对象设置为有信号状态

} 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 一旦互斥对象处于有信号状态,则该函数返回

{

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;

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • 2016年国庆假期终于把此书过完,整理笔记和体会于此。 关于书名 书名源于俄罗斯的演员斯坦尼斯拉夫斯基创作的《演员...
    李剑飞的简书阅读 7,218评论 2 65
  • 01. 清远镇内无庄稼,田地中均是成片成片数十年头甚至上百年的柿子树,如今秋日里,柿子树叶红胜火,果实挂满枝头,远...
    挺洋气一姑娘阅读 748评论 1 2
  • 爱情是永恒的主题,是男女之间情感高尚的表现 真正的爱情是1314式的 随着现在社会的发展, 离婚了增高,网络上 1...
    战福刚阅读 914评论 0 0
  • 前言 如果问一个iOS开发的同学,你们的网络请求是使用的什么框架呢?不用怀疑,十有八九都会回答使用的是AFNetw...
    Doctor_Peng阅读 609评论 2 2
  • 夏天夜跑 冬季游泳 想学滑雪 多喝水 擦芦荟胶 敷面膜 画眉毛 剃毛 皮肤保养 运动是一种涉及体力和技巧的由一套规...
    龙G阅读 255评论 0 1