MFC:通过代码简单理解进程间的通讯机制——共享内存

下面用共享映射文件的方式实现进程间通信,代码可以运行。

一、浅理解
每个进程有自己独立的空间,一个进程无法访问其他进程的数据。就好像两个是互不干涉的个体,想让它们进行通信(交换数据),就必须有一段它们都可以访问到的空间,作为中间介质。在计算机中,可以存放数据的地方分为内存和硬盘,进程是运行着的程序,肯定在内存当中。为让进程A和进程B进行通信,它们都可以访问的空间可以是

内存中它们以外的区域
硬盘中的区域
内存文件映射是将硬盘中的一个文件映射到内存中,进程A,B都可以访问该内存(文件),达到交换数据的目的。如右图是给用户的直接感觉,两个进程操作同一个物理文件,通过文件的读写,交换数据。


二、发送方(服务器)
个人理解,虽然共享内存都可以读写,也没有服务器和客户端的概念。但是,需要有一方创建这个文件,而另一方只需要获取并打开这个文件。为了方便,将创建文件的一方称作服务器,而获取并打开文件的一方称为客户端。而事实上,服务器或者客户端都可以对文件进行读写,类似于网络编程中的客户端和服务器都可以发消息接消息。自己写的服务器端C++代码如下

#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#pragma warning(disable:4996)
int_tmain(intargc, _TCHAR* argv[]){
    HANDLEhFile =CreateFile(TEXT("c:\zj.dat"),GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==NULL){
        printf("create file error!");
        return0;
    }
    // HANDLE hFile = (HANDLE)0xffffffff; //创建一个进程间共享的对象
    HANDLEhMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,  0,1024*1024,TEXT("ZJ"));
    intrst = GetLastError();
    if(hMap != NULL && rst == ERROR_ALREADY_EXISTS){
        printf("hMap error\n");
        CloseHandle(hMap);
        hMap = NULL;
        return0;
    }
    CHAR* pszText=NULL;
    pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
    if(pszText==NULL){
        printf("view map error!");
        return0;
    }
    sprintf(pszText,"hello my first mapping file!\n"); //其实是向文件中(共享内存中)写入了
    while(1){
        printf(pszText);
        Sleep(3000);
    }
    getchar();
    UnmapViewOfFile((LPCVOID)pszText);
    CloseHandle(hMap);
    CloseHandle(hFile);
    return0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
当调用 CreateFileMapping 创建命名的内存映射文件对象时 , Windows 即在物理内存申请一块指定大小的内存区域 , 返回文件映射对象的句柄 hMap ; 为了能够访问这块内存区域必须调用 MapViewOfFile 函数 , 促使 Windows 将此内存空间映射到进程的地址空间中 ; 当在其他进程访问这块内存区域时 , 则必须使用 OpenFileMapping 函数取得对象句柄 hMap , 并调用 MapViewOfFile 函数得到此内存空间的一个映射 , 这样系统就把同一块内存区域映射到了不同进程的地址空间中 , 从而达到共享内存的目的 。

先创建一个文件 CreateFile
HANDLE hFile = CreateFile(...);
参数可参见MSDN,就是创建一般的文件,此处不详说。个人认为这个文件的目的,就是共享内存的实体(也就是对应的硬盘中的区域)。
CreateFile(TEXT("c:\zj.dat"),GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
     在C盘的确有文件zj.dat 该文件名可以随意取。
创建内存映射文件 CreateFileMapping
    将上述硬盘中的文件hFile映射成为一个虚拟的映射文件 hMap ,即将物理文件与虚拟文件绑定,或者理解成将硬盘中的文件映射到内存当中。
HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0,1024*1024,TEXT("ZJ"));
     参数解释: hFile是对应的物理文件的句柄。如果hFile=NULL,即没有通过CreateFile创建一个实际存在的文件。有解释为创建一个进程间共享的对象。个人认为也可能是在内存中随机开辟了一段空间,或者在硬盘上有一个默认文件。
NULL: 安全属性
PAGE_READWRITE: 可读可写
0, 1024*1024: 从物理文件的高0位到低,1024*1024为映射成虚拟文件。(个人是这样理解的)
ZJ :是虚拟文件的名字/标识,客户端读时也用此来作为文件的标识,所以,这个名字应该是在进程外部注册的。
加载并获得内存映射文件 MapViewOfFile 在内存中的地址
     将虚拟文件映射成内存地址,方便使用。即将文件与内存绑定,以后由此地址访问该块内存。
CHAR* pszText=NULL; //一个指针,不需要分配空间
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024); //通过映射后,该指针就指向该文件,如果最后一个参数是0表示映射整个文件。
    参数解释:hMap作为虚拟文件的标识(通俗点就是虚拟文件名);
FILE_MAP_ALL_ACCESS:设定访问模式;
0,0:从虚拟文件的哪个位置开始映射成内存
1024*1024: 映射的内存大小
使用内存,即使用文件
    可以向这个内存(文件)读写数据了。
//写:
sprintf(pszText,"hello my first mapping file!\n");
    语句本身的意思,是将句子写入字符串pszText中,而这个字符串并没有在程序中分配空间,即没有new。但这句话也不会报错。是因为该字符串地址指向了映射文件,即通过操作该指针,实际是操作了文件,句子写入了文件当中。
//读:
printf(pszText);
    语句本身是将字符串中的内容输出到屏幕上,该字符串中没有分配空间,也没有赋值。但通过映射为文件,可以将文件中的内容通过该指针输出到屏幕上。
     注:个人理解,可以认为该指针pszText直接指向了硬盘空间(即通过内存文件和硬盘文件直接联系在一起)。就是文件的操作符。
5. 载映射 :
UnmapViewOfFile((LPCVOID)pszText);//卸载映射
6. 关闭文件:
"__mceDel">CloseHandle(hMap); //关闭虚拟文件
"__mceDel">CloseHandle(hMap); //关闭虚拟文件
三、接收方(客户端)
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));  
if(hMap == NULL){        
printf("open file map error!");
return0;
}    
CHAR* pszText = NULL;    
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);    if(pszText==NULL){        
printf("map view error!\n");        
return0;    
}    
printf(pszText); //从文件中读(共享内存)         
sprintf(pszText,"second data!\n"); //写入    
getchar();    
UnmapViewOfFile(pszText);    
CloseHandle(hMap);    
hMap = NULL;     
return0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
打开内存映射文件(虚拟文件)
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,TEXT("ZJ"));
通过内存映射文件名 ZJ 找到该文件的。
获得地址
CHAR* pszText = NULL;
pszText = (CHAR*)MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,1024*1024);
    映射成内存 MapViewOfFile和服务器中的一样。
使用内存
printf(pszText); //从文件中读(共享内存)
    读写数据,此处写入数据后,从服务器中读出来的内容就改变了。
    可以证明,服务器客户端同时使用着同一个文件,文件是它们之间的一个通道,用来交换数据
关闭映射,关闭文件。同服务器
UnmapViewOfFile(pszText);
CloseHandle(hMap);
四、注意点
服务器和客户端必须同时为进程,即都在运行的时候,才可以交换数据。
    虽然是通过物理文件,交互数据的,但是ZJ是虚拟文件的名字,该名字必须在两个进程中都能认识,才可以通过它来交互数据。
     所以,如果服务器先打开,写入文件后,关闭。 客户端,再打开文件,则CreateFileMap会失败,也无法进行文件映射了。就像网络通信一样,必须双方都在,才可以通信。
当然客户端也可以通过CreateFile打开一个存在的文件 zj.dat ,再用CreateFileMap去映射,同样可以访问文件中的数据。
内存映射文件的机制是单纯的让访问文件变的简单。
    就好像文件流fstream模拟了输入输出流iostream一样,操作文件,就像操作cout,cin一样方便。
    同理,通过文件映射,操作文件就可以向操作内存一样方便。只是将这个机制用于进程间通信时,才需要考虑一端发送,一端接收,同步问题等。
共享内存中常存放结构体数组
    为了在各个进程当中共享数据,常常将自定义的结构体数组放到共享内存中(也就是说共享内存经常只存放一个变量,而这个变量是一个结构体数组)以此实现进程间数据的读写。本博客以字符串为例进行存储,将来有时间再修改程序。
共享内存中结构体中的字符串变量
由于共享内存当中需要直接开辟内存大小,存放结构体数组,每个结构体数组的内存大小是相同且固定的。因此结构体当中不能存放CString之类的不定长数据类型,否则在赋值的时候会报错(调试的时候可以发现,CString类型的变量此时所占内存大小为NULL)。可以用char来代替CString(如用char str[30]代替CSring str)。
--------------------- 

原文:https://blog.csdn.net/dashumak/article/details/83107889 
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值