C++共享内存小白入门指南

什么是共享内存?

想象一下,你和你的室友共用一个冰箱。你们都可以往里面放东西,也可以从里面拿东西,这就是共享内存的基本概念!在C++中,共享内存是一种让不同进程(可以理解为不同的程序)能够访问同一块内存区域的技术。

为什么要使用共享内存?

  1. 高效通信:进程间通信最快的方式之一
  2. 数据共享:多个程序可以访问相同数据
  3. 减少复制:不需要在不同进程间复制大量数据

基础知识准备

在深入之前,你需要知道:

  • 进程:正在运行的程序实例
  • 内存:程序存储数据的地方
  • 系统调用:操作系统提供的功能接口

共享内存使用步骤(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;
}

注意事项和最佳实践

  1. 同步问题:多个进程同时访问时需要同步机制(如信号量)
  2. 内存泄漏:确保正确释放共享内存
  3. 安全性:共享内存对所有有权限的进程可见
  4. 大小限制:系统对共享内存大小有限制

常见问题解答

Q: 共享内存和普通内存有什么区别? A: 共享内存可以被多个进程访问,普通内存只能被创建它的进程访问。

Q: 进程退出后共享内存还在吗? A: 除非显式删除,否则共享内存会一直存在。

Q: 如何防止数据冲突? A: 使用同步机制,如互斥锁、信号量等。

Q: 共享内存大小有限制吗? A: 有,取决于系统配置,通常可以通过系统命令查看和调整。

学习建议

  1. 从简单的示例开始,先理解基本概念
  2. 实践编写两个进程通信的程序
  3. 学习同步机制,处理并发访问
  4. 查阅系统文档,了解具体限制和特性

记住,共享内存是强大的工具,但需要谨慎使用。确保你理解了同步和清理的重要性,避免内存泄漏和数据损坏。祝你学习顺利!

相关推荐
码事漫谈2 小时前
C++程序崩溃时内存泄漏的真相
后端
程序员爱钓鱼2 小时前
Node.js 编程实战:数据库连接池与性能优化
javascript·后端·node.js
青鸟2182 小时前
从资深开发到脱产管理的心态转变
后端·算法·程序员
程序员爱钓鱼2 小时前
Node.js 编程实战:Redis缓存与消息队列实践
后端·面试·node.js
老华带你飞2 小时前
建筑材料管理|基于springboot 建筑材料管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习·spring
Linux编程用C2 小时前
Docker+Vscode搭建(本地/远程)开发环境
vscode·后端·docker
用户21991679703913 小时前
.Net通过EFCore和仓储模式实现统一数据权限管控并且相关权限配置动态生成
后端·github
用户47949283569153 小时前
node_modules 太胖?用 Node.js 原生功能给依赖做一次大扫除
前端·后端·node.js
开心就好20253 小时前
苹果iOS设备免越狱群控系统完整使用指南与应用场景解析
后端