什么是共享内存?
想象一下,你和你的室友共用一个冰箱。你们都可以往里面放东西,也可以从里面拿东西,这就是共享内存的基本概念!在C++中,共享内存是一种让不同进程(可以理解为不同的程序)能够访问同一块内存区域的技术。
为什么要使用共享内存?
- 高效通信:进程间通信最快的方式之一
- 数据共享:多个程序可以访问相同数据
- 减少复制:不需要在不同进程间复制大量数据
基础知识准备
在深入之前,你需要知道:
- 进程:正在运行的程序实例
- 内存:程序存储数据的地方
- 系统调用:操作系统提供的功能接口
共享内存使用步骤(Linux/Unix系统)
第一步:创建共享内存
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
int main() {
// 生成一个唯一的key
key_t key = ftok("shmfile", 65);
// 创建共享内存段,1024字节大小
// IPC_CREAT表示创建,0666是权限(可读可写)
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
if (shmid < 0) {
std::cerr << "创建共享内存失败" << std::endl;
return 1;
}
std::cout << "共享内存创建成功,ID: " << shmid << std::endl;
return 0;
}
第二步:附加到共享内存
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <cstring>
int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, 0666);
// 附加到共享内存
char* shared_memory = (char*)shmat(shmid, nullptr, 0);
if (shared_memory == (char*)-1) {
std::cerr << "附加到共享内存失败" << std::endl;
return 1;
}
// 写入数据到共享内存
std::strcpy(shared_memory, "你好,共享内存!");
std::cout << "数据已写入共享内存" << std::endl;
// 分离共享内存(但不删除)
shmdt(shared_memory);
return 0;
}
第三步:读取共享内存数据
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, 0666);
// 附加到共享内存
char* shared_memory = (char*)shmat(shmid, nullptr, 0);
std::cout << "从共享内存读取: " << shared_memory << std::endl;
// 分离共享内存
shmdt(shared_memory);
return 0;
}
第四步:删除共享内存
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, 0666);
// 删除共享内存段
shmctl(shmid, IPC_RMID, nullptr);
std::cout << "共享内存已删除" << std::endl;
return 0;
}
完整示例:进程间通信
写入进程(writer.cpp)
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <cstring>
#include <unistd.h>
int main() {
// 生成key
key_t key = ftok("shmfile", 65);
// 创建共享内存(1KB大小)
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
// 附加到共享内存
char* str = (char*)shmat(shmid, nullptr, 0);
std::cout << "写入进程启动,等待输入..." << std::endl;
// 连续写入数据
for (int i = 1; i <= 5; i++) {
sprintf(str, "消息 %d", i);
std::cout << "写入: " << str << std::endl;
sleep(2); // 等待2秒
}
// 写入结束标志
strcpy(str, "结束");
// 分离共享内存
shmdt(str);
return 0;
}
读取进程(reader.cpp)
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <iostream>
#include <cstring>
#include <unistd.h>
int main() {
// 生成相同的key
key_t key = ftok("shmfile", 65);
// 获取共享内存
int shmid = shmget(key, 1024, 0666);
// 附加到共享内存
char* str = (char*)shmat(shmid, nullptr, 0);
std::cout << "读取进程启动,等待数据..." << std::endl;
// 持续读取数据
while (true) {
std::cout << "读取: " << str << std::endl;
// 检查是否收到结束标志
if (strcmp(str, "结束") == 0) {
break;
}
sleep(1); // 每秒检查一次
}
// 分离共享内存
shmdt(str);
// 删除共享内存段
shmctl(shmid, IPC_RMID, nullptr);
std::cout << "共享内存已清理" << std::endl;
return 0;
}
编译和运行
bash
# 编译写入进程
g++ writer.cpp -o writer
# 编译读取进程
g++ reader.cpp -o reader
# 打开两个终端,先运行写入进程
./writer
# 在另一个终端运行读取进程
./reader
Windows下的共享内存(简单示例)
cpp
#include <windows.h>
#include <iostream>
#include <string>
int main() {
// 创建共享内存
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 使用系统页面文件
nullptr, // 默认安全属性
PAGE_READWRITE, // 可读可写
0, // 高位文件大小
256, // 低位文件大小(256字节)
L"MySharedMemory"); // 共享内存名称
// 映射到进程地址空间
char* pBuf = (char*)MapViewOfFile(
hMapFile, // 共享内存句柄
FILE_MAP_ALL_ACCESS, // 可读可写访问
0,
0,
256);
// 使用共享内存
strcpy(pBuf, "Windows共享内存示例");
std::cout << "写入: " << pBuf << std::endl;
// 清理
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
注意事项和最佳实践
- 同步问题:多个进程同时访问时需要同步机制(如信号量)
- 内存泄漏:确保正确释放共享内存
- 安全性:共享内存对所有有权限的进程可见
- 大小限制:系统对共享内存大小有限制
常见问题解答
Q: 共享内存和普通内存有什么区别? A: 共享内存可以被多个进程访问,普通内存只能被创建它的进程访问。
Q: 进程退出后共享内存还在吗? A: 除非显式删除,否则共享内存会一直存在。
Q: 如何防止数据冲突? A: 使用同步机制,如互斥锁、信号量等。
Q: 共享内存大小有限制吗? A: 有,取决于系统配置,通常可以通过系统命令查看和调整。
学习建议
- 从简单的示例开始,先理解基本概念
- 实践编写两个进程通信的程序
- 学习同步机制,处理并发访问
- 查阅系统文档,了解具体限制和特性
记住,共享内存是强大的工具,但需要谨慎使用。确保你理解了同步和清理的重要性,避免内存泄漏和数据损坏。祝你学习顺利!