跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Linux System V 共享内存:原理、实操与常见陷阱

System V 共享内存是 Linux 进程间通信的高效方式,直接共享物理内存避免拷贝开销。 ftok、shmget 等核心系统调用原理,提供 C++ 封装类实战代码,分析内核管理结构,并总结内存泄漏、同步缺失等常见陷阱及解决方案。

asphyx_a发布于 2026/3/29更新于 2026/7/2038 浏览
Linux System V 共享内存:原理、实操与常见陷阱

System V 共享内存是 Linux 进程间通信(IPC)中效率最高的方式之一。它允许不同进程直接访问同一块物理内存区域,省去了内核与用户空间之间的数据拷贝开销。作为经典的 IPC 机制,它在底层开发和高性能场景中依然广泛应用。

一、什么是 System V 共享内存?

System V 共享内存属于 System V 系列 IPC 机制的一部分,核心作用是让多个进程访问同一块物理内存。可以把它想象成一个公共仓库,系统划分出一块专属区域,给多个进程发放'访问权限',它们可以直接读写这块区域,无需通过内核缓冲区中转。

1.1 关键特点

  • 高效性:无内核与用户空间的数据拷贝,直接操作物理内存,是所有 IPC 机制中速度最快的。
  • 持续性:共享内存一旦创建,会一直存在于内核中,直到被显式删除或系统重启,不受进程生命周期影响。
  • 非实时性:没有自带的同步机制,多个进程同时读写时,需要手动搭配信号量等同步手段,否则会出现数据混乱。
  • 标识符(key):每个 System V 共享内存都有一个唯一的 key 值,进程通过 key 找到对应的共享内存。

1.2 核心理解

创建和'删除'shm 需要系统调用,而使用 shm 不需要(类似 malloc)。用户空间最有代表的就是用户可以直接访问指针。

二、底层原理与核心 API

2.1 内核管理数据结构

内核通过 struct shmid_ds 管理共享内存的属性。结合 Linux 内核源码,核心字段如下:

struct shmid_ds {
    struct ipc_perm shm_perm; // 权限控制结构体
    size_t shm_segsz;         // 共享内存大小(字节)
    pid_t shm_cpid;           // 创建进程 PID
    pid_t shm_lpid;           // 最后一次操作该内存的进程 PID
    unsigned short shm_nattch;// 当前挂载到该内存的进程数
    time_t shm_atime;         // 最后一次挂载时间
    time_t shm_dtime;         // 最后一次脱离时间
    time_t shm_ctime;         // 最后一次属性修改时间
    void *shm_unused2;        // 预留字段
};

struct ipc_perm 是 System V IPC 的通用权限结构体,内核通过该结构体的 key 字段唯一标识一个 IPC 资源。

2.2 核心系统调用

System V 共享内存的使用流程遵循'生成 Key→创建/获取→挂载→读写→脱离→删除'。

ftok:生成唯一 Key

用于将文件路径 + 项目 ID 转换为唯一的 key_t 类型值,作为共享内存的全局标识。

#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
  • pathname:必须是系统中已存在的文件路径。
  • proj_id:非 0 的 8 位整数,不同的 proj_id 会生成不同的 key。
  • 返回值:成功返回唯一 key,失败返回 -1。
shmget:创建 / 获取共享内存

返回共享内存标识符(shmid),后续操作均通过 shmid 关联。

#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
  • key:ftok 生成的唯一 Key。
  • size:共享内存大小(建议为 4096 的整数倍)。
  • shmflg:权限标志组合,如 IPC_CREAT(不存在则创建)、IPC_EXCL(确保全新创建)及权限位(如 0666)。
shmat:挂载共享内存

将共享内存映射到当前进程的虚拟地址空间,返回映射后的虚拟地址指针。

#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
  • shmaddr:指定挂载的虚拟地址(NULL 表示由内核自动分配)。
  • shmflg:挂载标志,如 SHM_RDONLY(只读)。
shmdt:脱离共享内存

将共享内存从当前进程的虚拟地址空间中解除映射关系,并非删除共享内存本身。

#include <sys/shm.h>
int shmdt(const void *shmaddr);
shmctl:控制共享内存

用于获取属性、修改属性或删除共享内存,是生命周期管理的核心 API。

#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • cmd:控制命令,如 IPC_STAT(获取属性)、IPC_SET(修改属性)、IPC_RMID(标记删除)。

三、实战代码示例

以下提供 C++ 封装类及读写进程示例,展示如何完整操作 System V 共享内存。

3.1 封装类核心逻辑(Shm.hpp)

该类封装了'生成 Key→创建/获取→挂载→删除→属性查询'的全流程。

#ifndef __SHM_HPP__
#define __SHM_HPP__
#include <iostream>
#include <cstdio>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>

const std::string proj_name = "/home";
const int proj_id = 0x6666;
const int g_size = 4096;

static std::string ToHex(long long data) {
    char buf[16];
    snprintf(buf, sizeof(buf), "0x%llx", data);
    return buf;
}

class Shm {
public:
    Shm(int size = g_size): _shmid(-1), _size(size), _key(0) {}
    ~Shm() {}

private:
    key_t GetKey() {
        _key = ftok(proj_name.c_str(), proj_id);
        if(_key < 0) { perror("ftok"); }
        return _key;
    }

    bool CreateCoreHelper(int flags) {
        key_t key = GetKey();
        _shmid = shmget(key, _size, flags);
        if(_shmid < 0) { perror("shmget"); return false; }
        return true;
    }

public:
    bool Create() { return CreateCoreHelper(IPC_CREAT | IPC_EXCL | 0666); }
    bool Get() { return CreateCoreHelper(IPC_CREAT); }
    bool Delete() { int n = shmctl(_shmid, IPC_RMID, nullptr); return n < 0 ? false : true; }
    void GetShmAttr() {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if(n < 0) { perror("shmctl"); return; }
        std::cout << ds.shm_cpid << std::endl;
        std::cout << ds.shm_segsz << std::endl;
        std::cout << ToHex(_key) << std::endl;
    }
    void *Attch() { _start = (char *)shmat(_shmid, nullptr, 0); return _start; }
    void Detach() { int n = shmdt(_start); (void)n; }
    void Debug() {
        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "size: " << _size << std::endl;
        std::cout << "key: " << ToHex(_key) << std::endl;
    }

private:
    int _shmid;
    int _size;
    key_t _key;
    char *_start;
};

typedef struct data {
    int count;
    char buf[26 * 2];
} buffer_t;
#endif

3.2 Writer 进程(Writer.cc)

演示获取并调试共享内存的状态。

#include "Shm.hpp"
#include <iostream>
#include <string>

int main() {
    Shm shm;
    shm.Get();
    shm.Debug();
    return 0;
}

3.3 Reader 进程(Reader.cc)

演示创建、挂载、读取属性及清理共享内存的流程。

#include "Shm.hpp"
#include <iostream>
#include <string>
#include <unistd.h>

int main() {
    Shm shm;
    shm.Create();
    sleep(3);
    shm.Attch();
    shm.Debug();
    shm.GetShmAttr();
    sleep(5);
    shm.Delete();
    return 0;
}

3.4 编译与运行

使用 Makefile 进行编译:

all:Writer Reader
Reader:Reader.cc
g++ -o $@ $^ -std=c++11
Writer:Writer.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f Writer Reader

运行步骤:先运行 ./Reader 创建内存,再运行 ./Writer 获取内存。注意测试时若设置大小为 4097,操作系统会向上对齐到 4096*2,可能导致浪费或越界风险。

四、内核管理机制

内核通过 struct ipc_ids 和 struct shmid_kernel 管理所有共享内存资源。全局维护 shm_ids 变量记录元数据,通过索引机制关联权限结构体,最终通过文件系统的 inode 和 vm_area_struct 实现物理内存与进程虚拟地址的映射。

五、避坑指南

虽然高效,但使用时容易踩坑,新手需重点关注:

  1. 内存泄漏:共享内存生命周期独立于进程。若进程异常退出未执行 shmctl(IPC_RMID),内存会一直占用。建议在程序中添加信号处理捕获 SIGINT,或在退出前强制删除。可通过 ipcs -m 查看,ipcrm -m <shmid> 删除。
  2. 数据混乱:没有自带同步机制。多个进程同时写入会导致覆盖。需搭配 System V 信号量或互斥锁实现互斥访问。
  3. Key 值不一致:不同进程使用 ftok() 生成 key 时,路径或标识不一致会导致无法找到同一块内存。确保所有进程使用完全相同的 KEY_PATH 和 KEY_ID。
  4. 大小设置不合理:不足会被截断,过大浪费资源。建议设置为页大小的整数倍。

六、总结

System V 共享内存的核心优势是'高效',劣势是'需手动管理同步和生命周期'。它适合高性能要求的进程间通信、大量数据频繁交换的场景以及底层嵌入式开发。现代 Linux 开发中也可关注 POSIX 共享内存(如 shm_open()、mmap()),但 System V 的兼容性和底层可控性依然是其不可替代的优势。

目录

  1. 一、什么是 System V 共享内存?
  2. 1.1 关键特点
  3. 1.2 核心理解
  4. 二、底层原理与核心 API
  5. 2.1 内核管理数据结构
  6. 2.2 核心系统调用
  7. ftok:生成唯一 Key
  8. shmget:创建 / 获取共享内存
  9. shmat:挂载共享内存
  10. shmdt:脱离共享内存
  11. shmctl:控制共享内存
  12. 三、实战代码示例
  13. 3.1 封装类核心逻辑(Shm.hpp)
  14. 3.2 Writer 进程(Writer.cc)
  15. 3.3 Reader 进程(Reader.cc)
  16. 3.4 编译与运行
  17. 四、内核管理机制
  18. 五、避坑指南
  19. 六、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 MCP 和 Skill 的前端 JS 逆向自动化落地实践
  • Flutter pathfinding 组件在 OpenHarmony 上的适配实战
  • MCP 协议详解:与 Function Call 的区别及实战使用
  • 大语言模型参数高效微调(PEFT)方法综述
  • 彻底关闭 Win10 中 Microsoft 365 Copilot 弹窗的 6 种方法
  • OpenClaw 接入飞书实战:让 AI 机器人读写文档与表格
  • 算法实战:利用前缀和解中心下标与数组乘积问题
  • OpenAI 发布 GPT-5.3 Instant:幻觉率降低及 2026 AI 模型排行
  • Stable Diffusion ADetailer 插件详解:修复人脸手部崩坏问题
  • Android 端实时语音转文字实战:基于 Whisper 的落地实践
  • Linux 系统学习:深入剖析 Git 原理与进阶使用
  • SkyWalking 接入 Spring Cloud Alibaba 微服务:从链路追踪到告警
  • Linux 命名管道(FIFO)通信:原理与跨进程实战
  • Manacher 算法详解:线性时间求解最长回文子串
  • 链表经典算法题解:相加、重排与合并
  • 基于阿里云ASR的AI电销机器人架构与实现
  • FLOAT:基于流匹配的音频驱动说话者头像生成模型
  • 基于 Higress 将 REST API 转换为 MCP Server 工具
  • Llama-2-7b 在昇腾 NPU 上的六大核心场景性能基准
  • 把查询延迟从2秒降到80毫秒:AI绘画关键词网站的优化历程

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online