背景
内存映射文件广泛应用于IPC(进程间通信),其原理是通过在内核空间中创建一个与磁盘文件的映射关系,各个进程读写该内存映射文件的操作与操作进程内的字符数组完全一致。
步骤
-
创建普通文件句柄。
主要包含指定访问方式(可读、可写等) -
根据文件句柄创建用于内存映射的内核对象句柄。
需要指明内核对象的容量大小和访问方式。
如果用于进程间通信,需要创建命名的内核对象,此名称的命名空间是属于操作系统层面的全局。 -
将内存映射文件映射到当前进程的地址空间
需要指定映射的偏移地址和映射的长度,此处容易出现由于偏移地址不符合特定偏移单元的倍数导致映射失败(GetLastError 1132) -
至此,内存映射文件已经完整的构造成功,按照字符数组的操作方式即可进行读写(是否可读、可写需依据创建句柄时给定的参数)。
-
进程间通信时,通常通过互斥量和信号量进行同步,通过内存映射文件进行数据通信。数据通信时一般创建命名的内存映射文件,这样在进程间创建内存映射文件时才能确保都连接至统一的内存文件中(总线)
-
退出时通过相反的顺序进行退出即可。
6.1 卸载内存映射文件至当前进程的映射
6.2 关闭映射文件句柄
6.3 关闭普通文件句柄 -
代码示例
如下代码通过标记区分当前是写端(读端),读端读取10次数据之后退出;写端写10此数据之后退出。
#include <Windows.h>
#include <iostream>
#include <memory>
#include <thread>
using namespace std;
int main(int argc, char** argv)
{
HANDLE hfile = CreateFile("MemFile.XXX", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hfile)
{
printf("Create File Success!\n");
}
else
{
return -1;
}
//
HANDLE hcoreFileMap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, 128, "TEST_FILE_XXTL");
if (hcoreFileMap == NULL)
{
CloseHandle(hfile);
printf("CreateFileMapping Failed.%u\n", GetLastError());
return -1;
}
LPVOID lpBuffer = MapViewOfFile(hcoreFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 32); // 20 - 21 - 40
if (lpBuffer == NULL)
{
printf("MapViewOfFile Failed.%u\n", GetLastError());
CloseHandle(hcoreFileMap);
CloseHandle(hfile);
return -1;
}
char* pBuffer = (char*)lpBuffer;
if (argc == 1) // 读值
{
thread t([&]()->void {
int i = 0;
pBuffer[0] = 0;
while (i < 10)
{
if (pBuffer[0] == 0)
{
// 没有数据
std::this_thread::sleep_for(std::chrono::milliseconds(200));
continue;
}
i++;
{
// 10 次
int nLen = pBuffer[0];
char* p = new char[nLen + 1];
strncpy(p, pBuffer + 1, nLen);
p[nLen] = 0;
printf("time %d %s\n",i, p);
delete[]p;
pBuffer[0] = 0; // 标记已经读取完成
}
}
printf("Process Read finished.\n");
});
t.join();
}
else
{
// 写值
thread t([&]()->void {
int i = 0;
pBuffer[0] = 0;
while (i < 10)
{
char ch[256] = { 0 };
scanf("%s", ch);
int nLen = strlen(ch);
if (nLen > 20)
{
ch[20] = 0;
nLen = 20;
}
strncpy(pBuffer + 1, ch, nLen);
pBuffer[0] = nLen;
while(pBuffer[0] != 0)
{
// 等待接收端接收
std::this_thread::sleep_for(std::chrono::milliseconds(200));
printf("wait once\n");
}
i++;
}
printf("Process Write finished.\n");
});
t.join();
}
printf("to exit Process\n");
UnmapViewOfFile(lpBuffer);
CloseHandle(hcoreFileMap);
CloseHandle(hfile);
return 0;
}