当前位置: 首页 > news >正文

进程间通信 之 共享内存

目录

前言

一、核心概念与本质

1.1什么是共享内存?

1.2 为什么是最快的IPC?

1.3 共享内存的特点

二、共享内存函数介绍

1.shmget

2.shmat

3.shmdt

4.shmctl

三、示例:双进程通信

1.写进程(writer.c)

2.读进程(reader.c)

四、命令行管理工具

1.查看共享内存:ipcs -m

2.删除共享内存:ipcrm -m

五、同步问题与解决方案

5.1 共享内存的最大缺陷

5.2解决方案:信号量配合


前言

共享内存是进程间通信(IPC)中效率最高的一种方式。它允许多个进程直接读写同一块物理内存区域,数据无需在内核和用户空间之间复制,从而实现了极快的数据交换。


一、核心概念与本质

1.1什么是共享内存?

共享内存是操作系统在物理内存中开辟的一块区域,多个进程可以将这块内存映射到自己的虚拟地址空间中。

一旦映射完成,进程就可以像访问自己的私有内存一样直接读写这块共享区域,无需系统调用介入。

如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他 进程看到。

1.2 为什么是最快的IPC?

与管道、消息队列等方式相比,共享内存的核心优势在于零拷贝:

通信方式数据流向拷贝次数
管道/消息队列进程A → 内核 → 进程B2次数据拷贝
共享内存进程A → 共享内存 → 进程B0次数据拷贝

管道的工作流程:

  • 写进程调用write(),将数据从用户空间拷贝到内核缓冲区
  • 读进程调用read(),将数据从内核缓冲区拷贝到用户空间

共享内存的工作流程:

  • 进程A直接往映射的虚拟地址写入数据(直接操作物理内存)
  • 进程B直接从同一地址读取数据(直接读取物理内存)
  • 全程无需内核介入,省去了两次拷贝开销

1.3 共享内存的特点

特性说明
高效性无需数据拷贝,是速度最快的IPC形式
全局性所有附加到该内存的进程均可访问
无同步机制内核不提供任何同步保护,需要用户自行解决
生命周期随内核,除非显式删除或系统重启
用户维护创建、使用、释放都需要用户手动管理

二、共享内存函数介绍

1.shmget

int shmget(key_t key, size_t size, int shmflg);

  • shmget()用于创建或者获取共享内存
  • shmget()成功返回共享内存的 ID, 失败返回-1
  • key: 不同的进程使用相同的 key 值可以获取到同一个共享内存,(这里的值和信号量的值一样也没有关系,因为类型不一样;)
  • size: 创建共享内存时,指定要申请的共享内存空间大小
  • shmflg: IPC_CREAT IPC_EXCL

2.shmat

void* shmat(int shmid, const void *shmaddr, int shmflg);

  • shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上
  • shmat()成功返回返回共享内存的首地址,失败返回 NULL(像malloc一样给共享空间的起始地址),看帮助手册失败返回的是-1;
  • shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间
  • shmflg: 一般给 0(给0就代表的可读可写), 可以给 SHM_RDONLY 为只读模式,其他的为读写

3.shmdt

int shmdt(const void *shmaddr);

  • shmdt()断开当前进程的 shmaddr 指向的共享内存映射
  • shmdt()成功返回 0, 失败返回-1
  • //为什么不是删除共享内存而是断开共享内存呢?因为你不使用了,
  • 别的进程可能还在使用这块共享内存;
  • //删除共享内存用shmctl,就是对共享内存做控制,
  • 那么可以设置,也可以删除,如下的函数;

4.shmctl

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

  • shmctl()控制共享内存
  • shmctl()成功返回 0,失败返回-1
  • cmd: IPC_RMID
  • //删除的时候如果还有人在使用共享内存,这个函数就会延迟删除,
  • 等到最后一个进程断开链接它才会删除共享内存.
  • //第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。

三、示例:双进程通信

1.写进程(writer.c)

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_SIZE 1024 // 共享内存大小 int main() { key_t key; int shmid; char *data; // 1. 生成key key = ftok("/tmp", 66); if (key == -1) { perror("ftok"); exit(1); } // 2. 创建共享内存 shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); if (shmid == -1) { perror("shmget"); exit(1); } printf("共享内存创建成功,shmid = %d\n", shmid); // 3. 挂接共享内存 data = (char *)shmat(shmid, NULL, 0); if (data == (char *)-1) { perror("shmat"); exit(1); } printf("共享内存挂接成功,地址 = %p\n", data); // 4. 写入数据 const char *message = "Hello from writer process!"; strcpy(data, message); printf("数据已写入: %s\n", message); // 5. 等待用户输入后退出(让读进程有机会读取) printf("按回车键退出并清理...\n"); getchar(); // 6. 解除挂接 shmdt(data); // 7. 删除共享内存 shmctl(shmid, IPC_RMID, NULL); printf("共享内存已删除\n"); return 0; }

2.读进程(reader.c)

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_SIZE 1024 int main() { key_t key; int shmid; char *data; // 1. 生成相同的key key = ftok("/tmp", 66); if (key == -1) { perror("ftok"); exit(1); } // 2. 获取已存在的共享内存(注意:不使用IPC_EXCL) shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666); if (shmid == -1) { perror("shmget"); exit(1); } printf("共享内存获取成功,shmid = %d\n", shmid); // 3. 挂接共享内存 data = (char *)shmat(shmid, NULL, 0); if (data == (char *)-1) { perror("shmat"); exit(1); } printf("共享内存挂接成功,地址 = %p\n", data); // 4. 读取数据 printf("读取到数据: %s\n", data); // 5. 解除挂接(不删除,让写进程负责清理) shmdt(data); return 0; }

四、命令行管理工具

ipcrm -m 12345 # 删除shmid为12345的共享内存

1.查看共享内存:ipcs -m

$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x2e0a0156 12345 user 666 1024 1

字段说明:

  • key:共享内存的键值
  • shmid:共享内存标识符
  • owner:创建者
  • perms:权限
  • bytes:大小
  • nattch:当前挂接的进程数

2.删除共享内存:ipcrm -m

ipcrm -m 12345 # 删除shmid为12345的共享内存

【注】:共享内存的生命周期随内核,即使创建它的进程已经退出,共享内存仍然存在,除非显式删除或系统重启

五、同步问题与解决方案

5.1 共享内存的最大缺陷

共享内存本身不提供任何同步机制。如果多个进程同时读写,会出现数据竞争问题:

5.2解决方案:信号量配合

通常使用信号量来保护共享内存的访问。

// 伪代码:生产者-消费者模式 #include <semaphore.h> sem_t *sem_empty; // 空位计数 sem_t *sem_full; // 数据计数 sem_t *sem_mutex; // 互斥锁 // 生产者 void producer() { sem_wait(sem_empty); // 等待有空位 sem_wait(sem_mutex); // 加锁 // 写入共享内存 sem_post(sem_mutex); // 解锁 sem_post(sem_full); // 增加数据计数 } // 消费者 void consumer() { sem_wait(sem_full); // 等待有数据 sem_wait(sem_mutex); // 加锁 // 读取共享内存 sem_post(sem_mutex); // 解锁 sem_post(sem_empty); // 增加空位计数 }
http://www.jsqmd.com/news/474221/

相关文章:

  • 从PX4到裸机NuttX:uORB消息总线的轻量化移植实战
  • 2026惠州实验室搬迁优质服务商推荐榜:惠州附近搬家公司、深圳仓库搬家公司、深圳仓库搬迁公司、深圳价格便宜搬家公司选择指南 - 优质品牌商家
  • 南北阁Nanbeige 4.1-3B与LaTeX结合:学术论文智能排版与润色工具链
  • 肯德基:有些公式改变了世界。有些则改变了鸡肉
  • douyin-downloader:短视频内容全场景管理与高效下载解决方案
  • FireRed-OCR Studio实操手册:OCR结果Markdown支持Mermaid图表嵌入
  • Web安全零基础学习
  • 文献翻译工具怎么选?研究生/博士生实测10款主流翻译软件,这款综合实力最强
  • wxauto:重新定义Windows微信自动化的技术实践指南
  • 全志T133-s3(Tina Linux)下5寸RGB屏驱动移植与LVGL优化实战
  • SAP-MM工厂配置实战:从基础搭建到智能物流的完整解决方案
  • GME多模态向量-Qwen2-VL-2B效果展示:跨文档关联图表与文字
  • 造相Z-Image模型v2批量生成技巧:自动化处理大量Prompt方案
  • 告别平台依赖:如何让Scratch作品独立运行于任何设备?
  • Face3D.ai Pro模型优化:使用卷积神经网络提升纹理细节
  • ClickHouse vs Doris vs Impala:三大MPP引擎实战选型指南(附性能对比表)
  • WPF 中的 <Window> 和 <Application>根级标签讲解
  • 4. 配置飞书接入openclaw
  • 【Light: Science Applications】颠覆传统电子计算!一张1.8mm芯片如何实现全光学图像处理?
  • 魔兽世界宏命令工具:让游戏操作效率提升10倍的开源解决方案
  • 春联生成模型-中文-base效果展示:十组关键词生成惊艳对联案例
  • Qwen Pixel Art保姆级教程:Gradio界面各参数含义与推荐取值范围
  • 告别复杂配置:M2FP镜像开箱即用,小白也能玩转人体语义分割
  • LongCat动物百变秀效果展示:看看这些猫咪戴皇冠、狗狗变狮子的惊艳案例
  • ChatTTS模型部署实战:从百度网盘下载models.tar.gz到生产环境避坑指南
  • C# 中的 TCP 与 UDP 网络编程
  • 函数的递归
  • 游戏库管理困境?这款开源工具让Steam数据掌控变简单
  • IDEA或DataGrip手动插入数据时报错when IDENTITY_INSERT is set to OFF的解决方法
  • 告别编译烦恼:Vcpkg一站式部署Tesseract-OCR C++开发环境(Windows)