进程通信的基本概念
进程通信就是不同进程之间互相传递数据和消息,让它们能协同工作。就像人们之间需要交流信息来合作完成任务一样,进程也需要通信来一起完成复杂的操作。常见的有以下几种:
- 管道 :就像两个进程之间拉了一根水管,数据可以在这根管子里单向流动。一个进程向管道写入数据,另一个进程从管道读取数据。
- 套接字 :类似于通信的 “隧道”,可以在不同设备上的进程之间进行网络通信。通过套接字,进程可以跨越网络传递数据。
- 共享内存 :效率极高,相当于给多个进程共享一块 “公共区域”,各进程可直接在此区域读写数据,快速实现数据交换。
我们这次重点说共享内存通信。之前我们讲过内存映射文件,它允许将文件内容映射到内存地址空间,使多个进程像操作内存一样读写文件内容,共享内存与之类似,只不过共享内存主要用于进程间通信,无须涉及文件操作,直接在内存中开辟共享区域供多个进程访问。
使用示例
核心步骤
- 1)取唯一名称 :为共享内存和事件对象取唯一名称,避免命名冲突。
- 2)映射内存 :使用
MapViewOfFile
将共享内存映射到进程地址空间。 - 3)读写数据 :按约定的数据结构进行读写操作。
- 4)发送通知 :生产者写完数据后,通过事件对象通知消费者。
数据结构定义
struct DataHeader {
short dataType; // 数据类型
short width; // 图像宽度
short height; // 图像高度
short frameIndex; // 帧号
};
const int SHM_SIZE = sizeof(DataHeader) + 20000 * sizeof(short);
const wchar_t* SHM_NAME = L"SuperSharingMemory";
const wchar_t* EVENT_NAME = L"DataIsReady";
生产者写数据
void ProducerWriteData(short* pixelData, int w, int h, int frame) {
// 1. 创建或打开共享内存
HANDLE hShm = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0, SHM_SIZE >> 31,
SHM_SIZE & 0xFFFFFFFF,
SHM_NAME
);
// 2. 映射共享内存到进程地址空间
char* shmAddr = (char*)MapViewOfFile(hShm, FILE_MAP_ALL_ACCESS, 0, 0, SHM_SIZE);
// 3. 填写数据头部信息
DataHeader* header = (DataHeader*)shmAddr;
header->dataType = 1;
header->width = (short)w;
header->height = (short)h;
header->frameIndex = (short)frame;
// 4. 写入像素数据
short* dataStart = (short*)(shmAddr + sizeof(DataHeader));// 计算像素数据起始地址
memcpy(dataStart, pixelData, w * h * sizeof(short));// 直接内存拷贝
// 5. 通知消费者数据已准备好
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, EVENT_NAME);
SetEvent(hEvent); // 触发事件(通知消费者)
CloseHandle(hEvent);// 关闭事件句柄
// 6. 清理资源(注意)
UnmapViewOfFile(shmAddr); // 解除内存映射
CloseHandle(hShm); // 关闭共享内存句柄
}
消费者读数据
void ConsumerReadData() {
// 1. 打开共享内存
HANDLE hShm = OpenFileMapping(FILE_MAP_READ, FALSE, SHM_NAME);
// 2. 映射共享内存到进程地址空间
char* shmAddr = (char*)MapViewOfFile(hShm, FILE_MAP_READ, 0, 0, SHM_SIZE);
// 3. 等待数据准备就绪的通知
HANDLE hEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, EVENT_NAME);
WaitForSingleObject(hEvent, INFINITE); // 阻塞等待事件变为有信号状态
// 4. 读取数据
DataHeader* header = (DataHeader*)shmAddr;
short* pixelData = (short*)(shmAddr + sizeof(DataHeader)); // 获取像素数据起始位置
printf("收到数据:宽度%d,高度%d,帧号%d\n", header->width, header->height, header->frameIndex);
// 5. 清理资源
UnmapViewOfFile(shmAddr); // 解除内存映射
CloseHandle(hShm); // 关闭共享内存句柄
CloseHandle(hEvent); // 关闭事件句柄
}
总结
共享内存通信相当于两个程序共用一块内存区域:
- 生产者写入数据后,通过事件通知消费者。
- 消费者收到通知后,从共享内存中读取数据。
- 整个过程数据无需拷贝,传输效率极高。