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. 查阅系统文档,了解具体限制和特性

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

相关推荐
一 乐1 小时前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
码事漫谈2 小时前
Protocol Buffers 编码原理深度解析
后端
码事漫谈2 小时前
gRPC源码剖析:高性能RPC的实现原理与工程实践
后端
踏浪无痕4 小时前
AI 时代架构师如何有效成长?
人工智能·后端·架构
程序员小假4 小时前
我们来说一下无锁队列 Disruptor 的原理
java·后端
武子康5 小时前
大数据-209 深度理解逻辑回归(Logistic Regression)与梯度下降优化算法
大数据·后端·机器学习
maozexijr5 小时前
Rabbit MQ中@Exchange(durable = “true“) 和 @Queue(durable = “true“) 有什么区别
开发语言·后端·ruby
源码获取_wx:Fegn08955 小时前
基于 vue智慧养老院系统
开发语言·前端·javascript·vue.js·spring boot·后端·课程设计
独断万古他化6 小时前
【Spring 核心: IoC&DI】从原理到注解使用、注入方式全攻略
java·后端·spring·java-ee
毕设源码_郑学姐6 小时前
计算机毕业设计springboot基于HTML5的酒店预订管理系统 基于Spring Boot框架的HTML5酒店预订管理平台设计与实现 HTML5与Spring Boot技术驱动的酒店预订管理系统开
spring boot·后端·课程设计