WIN32 进程间通讯-共享内存

2024-06-06 23:58

本文主要是介绍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_READWRITE00x1000" zzj")

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

例:映射缓存区视图
void* m_pBaseMapFile=MapViewOfFile(m_hMapFile

FILE_MAP_READ|FILE_MAP_WRITE
00
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_WRITE00
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, //
安全属性(只对NT2000生效)

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
4) 
共享内存名称

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

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

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

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

服务端:

 

[cpp]  view plain copy
print ?
  1. #include "stdafx.h"  
  2. #include <Windows.h>  
  3. #include <iostream>  
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     HANDLE hMutex           = NULL;  
  9.     HANDLE hFileMapping     = NULL;  
  10.     LPVOID lpShareMemory    = NULL;  
  11.     HANDLE hServerWriteOver = NULL;  
  12.     HANDLE hClientReadOver  = NULL;  
  13.   
  14.     //create share memory  
  15.     hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,  
  16.         NULL,  
  17.         PAGE_READWRITE,  
  18.         0,  
  19.         1024*1024,  
  20.         L"ShareMemoryTest");  
  21.     if (NULL == hFileMapping)  
  22.     {  
  23.         cout << "CreateFileMapping fail:" << GetLastError() << endl;  
  24.         goto SERVER_SHARE_MEMORY_END;  
  25.     }  
  26.   
  27.     lpShareMemory = MapViewOfFile(hFileMapping,  
  28.         FILE_MAP_ALL_ACCESS,  
  29.         0,  
  30.         0,      //memory start address  
  31.         0);     //all memory space  
  32.     if (NULL == lpShareMemory)  
  33.     {  
  34.         cout << "MapViewOfFile" << GetLastError() << endl;  
  35.         goto SERVER_SHARE_MEMORY_END;  
  36.     }  
  37.   
  38.     //373  
  39.     hMutex = CreateMutex(NULL, FALSE, L"SM_Mutex");  
  40.     if (NULL == hMutex || ERROR_ALREADY_EXISTS == GetLastError())  
  41.     {  
  42.         cout << "CreateMutex" << GetLastError() << endl;  
  43.         goto SERVER_SHARE_MEMORY_END;  
  44.     }//多个线程互斥访问  
  45.   
  46.     //send data  
  47.     hServerWriteOver = CreateEvent(NULL,  
  48.         TRUE,  
  49.         FALSE,  
  50.         L"ServerWriteOver");  
  51.     hClientReadOver = CreateEvent(NULL,  
  52.         TRUE,  
  53.         FALSE,  
  54.         L"ClientReadOver");  
  55.     if (NULL == hServerWriteOver ||  
  56.         NULL == hClientReadOver)  
  57.     {  
  58.         cout << "CreateEvent" << GetLastError() << endl;  
  59.         goto SERVER_SHARE_MEMORY_END;  
  60.     }  
  61.   
  62.     char p = 0;  
  63.     char* q = (char*)lpShareMemory;  
  64.     do   
  65.     {  
  66.         p = getchar();  
  67.         if (WaitForSingleObject(hClientReadOver, 5*1000) != WAIT_OBJECT_0)   
  68.             goto SERVER_SHARE_MEMORY_END;  
  69.         q[0] = p;  
  70.         if (!ResetEvent(hClientReadOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为无信号状态  
  71.         if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为有信号状态  
  72.     } while (p != '\n');  
  73.   
  74. SERVER_SHARE_MEMORY_END:  
  75.     //release share memory  
  76.     if (NULL != hServerWriteOver)   CloseHandle(hServerWriteOver);  
  77.     if (NULL != hClientReadOver)    CloseHandle(hClientReadOver);  
  78.     if (NULL != lpShareMemory)      UnmapViewOfFile(lpShareMemory);  
  79.     if (NULL != hFileMapping)       CloseHandle(hFileMapping);  
  80.     if (NULL != hMutex)             ReleaseMutex(hMutex);  
  81.     return 0;  
  82. }  


客户端:

[cpp]  view plain copy
print ?
  1. #include "stdafx.h"  
  2. #include <Windows.h>  
  3. #include <iostream>  
  4. using namespace std;  
  5. int main()  
  6. {  
  7.     HANDLE hMutex           = NULL;  
  8.     HANDLE hFileMapping     = NULL;  
  9.     LPVOID lpShareMemory    = NULL;  
  10.     HANDLE hServerWriteOver = NULL;  
  11.     HANDLE hClientReadOver  = NULL;  
  12.   
  13.     hMutex = OpenMutex(MUTEX_ALL_ACCESS,  
  14.         FALSE,  
  15.         L"SM_Mutex");  
  16.     if (NULL == hMutex)  
  17.     {  
  18.         if (ERROR_FILE_NOT_FOUND == GetLastError())  
  19.         {  
  20.             cout << "OpenMutex fail: file not found!" << endl;  
  21.             goto CLIENT_SHARE_MEMORY_END;  
  22.         }  
  23.         else  
  24.         {  
  25.             cout << "OpenMutex fail:" << GetLastError() << endl;  
  26.             goto CLIENT_SHARE_MEMORY_END;  
  27.         }  
  28.     }  
  29.   
  30.     if (WaitForSingleObject(hMutex, 5000) != WAIT_OBJECT_0)//hMutex 一旦互斥对象处于有信号状态,则该函数返回  
  31.     {  
  32.         DWORD dwErr = GetLastError();  
  33.         goto CLIENT_SHARE_MEMORY_END;  
  34.     }  
  35.   
  36.     //open share memory  
  37.     hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,  
  38.         FALSE,  
  39.         L"ShareMemoryTest");  
  40.     if (NULL == hFileMapping)  
  41.     {  
  42.         cout << "OpenFileMapping" << GetLastError() << endl;  
  43.         goto CLIENT_SHARE_MEMORY_END;  
  44.     }  
  45.   
  46.     lpShareMemory = MapViewOfFile(hFileMapping,  
  47.         FILE_MAP_ALL_ACCESS,  
  48.         0,  
  49.         0,  
  50.         0);  
  51.     if (NULL == lpShareMemory)  
  52.     {  
  53.         cout << "MapViewOfFile" << GetLastError() << endl;  
  54.         goto CLIENT_SHARE_MEMORY_END;  
  55.     }  
  56.   
  57.     //read and write data  
  58.     hServerWriteOver = CreateEvent(NULL,  
  59.         TRUE,  
  60.         FALSE,  
  61.         L"ServerWriteOver");  
  62.     hClientReadOver = CreateEvent(NULL,  
  63.         TRUE,  
  64.         FALSE,  
  65.         L"ClientReadOver");  
  66.     if (NULL == hServerWriteOver ||  
  67.         NULL == hClientReadOver)  
  68.     {  
  69.         cout << "CreateEvent" << GetLastError() << endl;  
  70.         goto CLIENT_SHARE_MEMORY_END;  
  71.     }  
  72.   
  73.     char p = 0;  
  74.     char* q = (char*)lpShareMemory;  
  75.     do   
  76.     {  
  77.         if (!SetEvent(hClientReadOver))   
  78.             goto CLIENT_SHARE_MEMORY_END;  
  79.   
  80.         if (WaitForSingleObject(hServerWriteOver, INFINITE) != WAIT_OBJECT_0)   
  81.             goto CLIENT_SHARE_MEMORY_END;   
  82.   
  83.         p = q[0];  
  84.         putchar(p);  
  85.         if (!ResetEvent(hServerWriteOver))   
  86.             goto CLIENT_SHARE_MEMORY_END;  
  87.     } while (p != '\n');  
  88.   
  89. CLIENT_SHARE_MEMORY_END:  
  90.     //release share memory  
  91.     if (NULL != hServerWriteOver)   CloseHandle(hServerWriteOver);  
  92.     if (NULL != hClientReadOver)    CloseHandle(hClientReadOver);  
  93.     if (NULL != lpShareMemory)      UnmapViewOfFile(lpShareMemory);  
  94.     if (NULL != hFileMapping)       CloseHandle(hFileMapping);  
  95.     if (NULL != hMutex)             ReleaseMutex(hMutex);  
  96.     return 0;  
  97. }  

这篇关于WIN32 进程间通讯-共享内存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1037599

相关文章

Android进程保活全攻略(中)

在上一篇博客Android进程保活全攻略(上)中介绍了进程保活的背景和一些方法的思路和实现方式,本篇博客我将承接上篇博客,继续进行介绍。 9) 1像素悬浮层 **思路:**1像素悬浮层是传说的QQ黑科技,监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕

Android进程保活全攻略(上)

对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思路,并未将实现代码展示,本次我的博客将分为上下两篇,阐述关于进程保活的所有方法,以及实现的方式,若有错漏之处,大家可以在博客进行留言。 ** 1.进程保活-背景知识 ** (1)什么时候系统会去杀死进程? Android系统会

Android Framework学习(三)之SyetemServer进程启动解析

从上篇博客中,我们知道了Zygote进程启动了SyetemServer进程,本篇博客我们就一起来学习SyetemServer进程。 SystemServer的作用 整个系统的android framework进程启动流程如下: init进程 –> Zygote进程 –> SystemServer进程 –>各种应用进程 SystemServer进程主要的作用是启动各种系统服务,比如Activ

Android Framework学习(二)之Zygote进程启动解析

上篇博客,我们学习了init进程的相关知识,本篇博客我们一次来学习zygote进程的相关知识。 Zygote简介 在Android系统中,JavaVM(Java虚拟机)、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动

Python17 多进程multiprocessing

1.多进程与多线程的区别 在Python中,多线程(multithreading)和多进程(multiprocessing)是两种并行执行任务的方式,它们有一些关键的区别: 进程和线程的基本区别: 进程:进程是操作系统分配资源和调度的基本单位,每个进程都有自己独立的内存空间和资源。多进程环境下,同一个程序可以运行在不同的内存地址空间中,进程之间不会相互干扰。 线程:线程是进程的一

Android匿名共享内存

ashmem的使用流程如下: 1.ashmem_create_region创建匿名共享内存区域,本质是调用open系统调用 2.ioctl设置共享内存的名字和大小,设置的名字为/dev/ashmem/,名字的存在就为了能够让其他人找到目标 3.mmap映射文件中的虚拟内存以及物理内存 4.直接对着这一块地址区域读写。 其中ioctl必须设置名字和大小,不然没办法进行映射,因为在映射之前进行了校验

Linux 7种 进程间通信方式

传统进程间通信         通过文件实现进程间通信 必须人为保证先后顺序        A--->硬盘---> B(B不知道A什么时候把内容传到硬盘中) 1.无名管道 2.有名管道 3.信号 IPC进程间通信 4.消息队列 5.共享内存 6.信号灯集 7.socket通信 一、无名管道(亲缘关系的进程   64K) 原理:         如果A和B进程想要通过无名管道

【Linux】进程间通信_1

文章目录 七、进程间通信1. 进程间通信分类管道 未完待续 七、进程间通信 进程间由于 进程具有独立性 ,所以不可以直接进行数据传递。但是我们通常需要多个进程协同,共同完成一件事,所以我们需要进程间通信的手段。进程间通信的本质就是先让不同的进程看到同一份资源,这个资源一般都是由操作系统提供。 1. 进程间通信分类 管道 父进程和子进程分别使用 r 和 w 方式打开同

腾讯Hardcoder-Android通讯框架简介

APP 的功能和业务特性不依赖于该框架。 总而言之,由于Hardcoder是腾讯主导的,所以我们不用太担心兼容性问题,腾讯会和手机厂商进行洽谈并提供解决方案,并且目前已经支持Hardcoder框架的手机厂商有OPPO、vivo、华为、小米、三星、魅族等。 Hardcoder 性能优化技术方案 Hardcoder 优化基础 Hardcoder 在Android系统侧主要优化的方法有提高 CP

linux下I/O模型并发的epoll多进程池协程实现

方法1 主要思路: 定义了一个EventData结构体,用于存储事件相关的数据,如文件描述符、epoll 文件描述符、协程 ID 等。EchoDeal函数用于处理请求消息,并生成响应消息。handlerClient函数是协程的执行函数,用于处理客户端连接。它通过循环读取数据、解析请求、执行业务处理、发送响应等步骤,实现了对客户端请求的处理。handler函数是主函数,用于创建监听套接字、初始化