【并发编程四】c++进程通信——共享内存(shared memmory)
- 一、共享内存(shared memmory)
- 二、共享内存的过程
- 1、processA 写入共享内存
- 2、processB 读取共享内存
- 三、代码demo
- 1、processA demo
- 2、processB demo
- 四、输出
- 五 、共享内存的问题
一、共享内存(shared memmory)
消息队列的读取和写⼊的过程,都会有发⽣⽤户态与内核态之间的消息拷⻉过程。那共享内存的⽅式,就很好的解决了这⼀问题。
现代操作系统,对于内存管理,采⽤的是虚拟内存技术,也就是每个进程都有⾃⼰独⽴的虚拟内存空间,不同进程的虚拟内存映射到不同的物理内存中。所以,即使进程 A中 和 进程 B中 的虚拟地址是⼀样的,其实访问的是不同的物理内存地址,对于数据的增删查改互不影响。
共享内存的机制,就是拿出⼀块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写⼊的东⻄,另外⼀个进程⻢上就能看到了,都不需要拷⻉来拷⻉去,传来传去,⼤⼤提⾼了进程间通信的速度。
二、共享内存的过程
我们以进程processA写,processB写为例,讲述下共享内存的过程
1、processA 写入共享内存
- CreateFileMapping: 创建命名的内存映射文件对象 , Windows 即在物理内存申请一块指定大小的内存区域 , 返回文件映射对象的句柄 hMap ;
- MapViewOfFile:为了能够访问这块内存区域 , 促使 Windows 将此内存空间映射到进程的地址空间中;
- 通过句柄,把信息写入共享内存。
2、processB 读取共享内存
- OpenFileMapping:打开共享内存。当在进程processB访问processA创建的内存区域时 , 则必须使用 OpenFileMapping 函数取得对象句柄hMap ,
- MapViewOfFile:得到此内存空间的一个映射 , 这样系统就把同一块内存区域映射到了本进程的地址空间中 ,
- 通过句柄,把共享内存信息的拷贝到进程的变量内。(其实,通过句柄,你已经可以看到共享内存的数据了,是否拷贝,是业务逻辑的事情)
这样就达到了共享内存的目的。
三、代码demo
1、processA demo
- 只要不关闭共享内存的句柄,此创建共享内存的进程还在,其他进程就可以读取共享内存。
// main.cpp
#include <windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
int main(int argc, TCHAR* argv[])
{
// 定义共享数据
char szBuffer[] = "Hello Shared Memory";
// 创建共享文件句柄
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 物理文件句柄
NULL, // 默认安全级别
PAGE_READWRITE, // 可读可写
0, // 高位文件大小
BUF_SIZE, // 地位文件大小
"ShareMemory" // 共享内存名称
);
// 映射缓存区视图 , 得到指向共享内存的指针
LPVOID lpBase = MapViewOfFile(
hMapFile, // 共享内存的句柄
FILE_MAP_ALL_ACCESS, // 可读写许可
0,
0,
BUF_SIZE
);
// 将数据拷贝到共享内存
strcpy((char*)lpBase, szBuffer);
cout << "存放入共享内存的数据:" << (char*)lpBase << endl;
// 解除文件映射
UnmapViewOfFile(lpBase);
system("pause"); //等待其他进程读取数据
// 关闭内存映射文件对象句柄,只要不关闭共享内存的句柄,此进程还在,其他进程就可以读取共享内存。
//CloseHandle(hMapFile);
return 0;
}
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)
PROJECT(process)
ADD_EXECUTABLE(processA main.cpp)
ADD_SUBDIRECTORY(processB)
SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
2、processB demo
#include <iostream>
#include <windows.h>
using namespace std;
#define BUF_SIZE 4096
int main()
{
cout << "processB" << endl << endl;
// 打开共享的文件对象
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, "ShareMemory");
if (hMapFile)
{
LPVOID lpBase = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
// 将共享内存数据拷贝出来
char szBuffer[BUF_SIZE] = { 0 };
strcpy(szBuffer, (char*)lpBase);
cout << "读取共享内存数据:" << szBuffer << endl;
// 解除文件映射
UnmapViewOfFile(lpBase);
// 关闭内存映射文件对象句柄
CloseHandle(hMapFile);
}
else
{
// 打开共享内存句柄失败
cout << "Open Mapping Error";
}
system("pause");
return 0;
}
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)
SET(TARGET "childprocess")
ADD_EXECUTABLE(processB main.cpp)
SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
四、输出
因为之前的文章都有使用cmake编译的指令,后面就不再单独介绍了。
cmake -B build
cmake --build build
输出
五 、共享内存的问题
⽤了共享内存通信⽅式,带来新的问题,那就是如果多个进程同时修改同⼀个共享内存,很有可能就冲突了。例如两个进程都同时写⼀个地址,那先写的那个进程会发现内容被别⼈覆盖了。
为了防⽌多进程竞争共享资源,⽽造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只
能被⼀个进程访问。正好,信号量就实现了这⼀保护机制。
下一篇介绍信号量。
参考:
1、图解操作系统。
2、Windows上C++使用共享内存进行进程间通讯