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

Linux 进程间通信:共享内存详解

文章目录

    • 1. 共享内存原理及使用场景
      • 原理
      • 使用场景
    • 2. 共享内存相关API接口
      • System V共享内存API
        • 1. `shmget` - 创建/获取共享内存段
        • 2. `shmat` - 附加共享内存段
        • 3. `shmdt` - 分离共享内存段
        • 4. `shmctl` - 控制共享内存段
      • POSIX共享内存API
        • 1. `shm_open` - 创建/打开共享内存对象
        • 2. `shm_unlink` - 删除共享内存对象
        • 3. `ftruncate` - 设置共享内存大小
        • 4. `mmap` - 内存映射
        • 5. `munmap` - 取消内存映射
    • 3. 实例代码
      • 示例1:System V共享内存
      • 示例2:POSIX共享内存
    • 4. 编译与运行
    • 注意事项

1. 共享内存原理及使用场景

原理

共享内存是最高效的进程间通信(IPC)方式之一,它允许多个进程访问同一块物理内存区域。其核心原理是:

  1. 在内存中创建一块共享区域
  2. 将这块内存映射到多个进程的地址空间
  3. 进程可以直接读写这块内存,无需内核干预

由于数据不需要在进程和内核之间复制,共享内存的通信速度非常快。

使用场景

共享内存适合以下场景:

  • 需要高性能的进程间通信
  • 需要传输大量数据
  • 进程间需要频繁交换数据
  • 对实时性要求高的应用

典型应用包括:

  • 数据库系统
  • 科学计算应用
  • 图形处理程序
  • 实时数据处理系统

2. 共享内存相关API接口

System V共享内存API

1. shmget - 创建/获取共享内存段
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
  • key: 共享内存键值,通常使用ftok生成或使用IPC_PRIVATE
  • size: 共享内存段大小(字节)
  • shmflg: 权限标志,常用组合:
    • IPC_CREAT | 0666:创建共享内存,权限为rw-rw-rw-
    • IPC_CREAT | IPC_EXCL | 0666:独占创建,若已存在则失败
  • 返回值:成功返回共享内存标识符,失败返回-1
2. shmat - 附加共享内存段
void *shmat(int shmid, const void *shmaddr, int shmflg);
  • shmid: shmget返回的标识符
  • shmaddr: 指定附加地址,通常为NULL让系统选择
  • shmflg:
    • SHM_RDONLY: 只读方式附加
    • 0: 读写方式附加
  • 返回值:成功返回附加后的地址指针,失败返回(void*)-1
3. shmdt - 分离共享内存段
int shmdt(const void *shmaddr);
  • shmaddr: shmat返回的地址指针
  • 返回值:成功返回0,失败返回-1
4. shmctl - 控制共享内存段
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • shmid: 共享内存标识符
  • cmd: 控制命令
    • IPC_STAT: 获取状态信息
    • IPC_SET: 设置参数
    • IPC_RMID: 删除共享内存段
  • buf: 指向shmid_ds结构的指针
  • 返回值:成功返回0,失败返回-1

POSIX共享内存API

1. shm_open - 创建/打开共享内存对象
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>int shm_open(const char *name, int oflag, mode_t mode);
  • name: 共享内存对象名称,以/开头
  • oflag: 标志位,类似open:
    • O_CREAT: 不存在则创建
    • O_RDWR: 读写方式打开
    • O_EXCL: 与O_CREAT一起使用,确保独占创建
  • mode: 权限位
  • 返回值:成功返回文件描述符,失败返回-1
2. shm_unlink - 删除共享内存对象
int shm_unlink(const char *name);
  • name: 共享内存对象名称
  • 返回值:成功返回0,失败返回-1
3. ftruncate - 设置共享内存大小
int ftruncate(int fd, off_t length);
  • fd: 共享内存文件描述符
  • length: 要设置的大小
  • 返回值:成功返回0,失败返回-1
4. mmap - 内存映射
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • addr: 映射地址,通常为NULL让系统选择
  • length: 映射长度
  • prot: 保护模式
    • PROT_READ: 可读
    • PROT_WRITE: 可写
  • flags:
    • MAP_SHARED: 共享映射
    • MAP_PRIVATE: 私有映射
  • fd: 文件描述符
  • offset: 文件偏移量
  • 返回值:成功返回映射地址,失败返回MAP_FAILED
5. munmap - 取消内存映射
int munmap(void *addr, size_t length);
  • addr: 映射地址
  • length: 映射长度
  • 返回值:成功返回0,失败返回-1

3. 实例代码

示例1:System V共享内存

writer.c - 写入共享内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define SHM_SIZE 1024  // 共享内存大小int main() {key_t key = ftok("shmfile", 65);  // 生成key// 创建共享内存段int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);if (shmid == -1) {perror("shmget failed");exit(EXIT_FAILURE);}// 附加到进程地址空间char *shm = (char*)shmat(shmid, NULL, 0);if (shm == (char*)-1) {perror("shmat failed");exit(EXIT_FAILURE);}printf("Write Data: ");fgets(shm, SHM_SIZE, stdin);  // 写入数据printf("Data written in memory: %s\n", shm);// 等待读取进程读取while (*shm != '*') {sleep(1);}// 分离共享内存shmdt(shm);// 删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}

reader.c - 从共享内存读取

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>#define SHM_SIZE 1024int main() {key_t key = ftok("shmfile", 65);  // 相同的key// 获取共享内存int shmid = shmget(key, SHM_SIZE, 0666);if (shmid == -1) {perror("shmget failed");exit(EXIT_FAILURE);}// 附加到进程地址空间char *shm = (char*)shmat(shmid, NULL, 0);if (shm == (char*)-1) {perror("shmat failed");exit(EXIT_FAILURE);}printf("Data read from memory: %s\n", shm);// 通知写入进程已完成读取*shm = '*';// 分离共享内存shmdt(shm);return 0;
}

示例2:POSIX共享内存

posix_writer.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>#define SHM_NAME "/my_shm"
#define SIZE 4096typedef struct {char data[SIZE];int ready;  // 1表示数据已准备好,0表示未准备好
} SharedData;int main() {int shm_fd;SharedData *shared_data;// 创建共享内存对象shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}// 配置共享内存大小if (ftruncate(shm_fd, sizeof(SharedData)) == -1) {perror("ftruncate failed");close(shm_fd);shm_unlink(SHM_NAME);exit(EXIT_FAILURE);}// 内存映射shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (shared_data == MAP_FAILED) {perror("mmap failed");close(shm_fd);shm_unlink(SHM_NAME);exit(EXIT_FAILURE);}// 初始化共享数据shared_data->ready = 0;printf("Writer: Enter data to write to shared memory: ");if (fgets(shared_data->data, SIZE, stdin) == NULL) {perror("fgets failed");munmap(shared_data, sizeof(SharedData));close(shm_fd);shm_unlink(SHM_NAME);exit(EXIT_FAILURE);}// 标记数据已准备好shared_data->ready = 1;printf("Writer: Data written to memory: %s", shared_data->data);// 等待读者读取完成printf("Writer: Waiting for reader to finish...\n");while (shared_data->ready == 1) {sleep(1);}printf("Writer: Reader has finished reading.\n");// 清理if (munmap(shared_data, sizeof(SharedData)) == -1) {perror("munmap failed");}if (close(shm_fd) == -1) {perror("close failed");}if (shm_unlink(SHM_NAME) == -1) {perror("shm_unlink failed");}return 0;
}

posix_reader.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>#define SHM_NAME "/my_shm"
#define SIZE 4096typedef struct {char data[SIZE];int ready;  // 1表示数据已准备好,0表示未准备好
} SharedData;int main() {int shm_fd;SharedData *shared_data;// 打开共享内存对象shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);if (shm_fd == -1) {perror("shm_open failed");exit(EXIT_FAILURE);}// 内存映射shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);if (shared_data == MAP_FAILED) {perror("mmap failed");close(shm_fd);exit(EXIT_FAILURE);}// 等待数据准备好printf("Reader: Waiting for data to be ready...\n");while (shared_data->ready == 0) {sleep(1);}// 读取数据printf("Reader: Data read from memory: %s", shared_data->data);// 通知写入者已完成读取shared_data->ready = 0;// 清理if (munmap(shared_data, sizeof(SharedData)) == -1) {perror("munmap failed");}if (close(shm_fd) == -1) {perror("close failed");}return 0;
}

4. 编译与运行

在这里插入图片描述

# 编译
gcc posix_writer.c -o posix_writer -lrt
gcc posix_reader.c -o posix_reader -lrt# 运行(需要先运行writer)
./posix_writer
# 在另一个终端
./posix_reader

注意事项

  1. 共享内存没有内置的同步机制,通常需要配合信号量或互斥锁使用

  2. 使用完毕后应及时释放资源,避免内存泄漏

  3. 在多进程访问时要注意数据一致性问题

  4. System V共享内存是持久的,即使没有进程附加也会保留,需要显式删除

  5. POSIX共享内存通常挂载在/dev/shm目录下
    在这里插入图片描述

http://www.lryc.cn/news/597965.html

相关文章:

  • Spring Boot 3整合Spring AI实战:9轮面试对话解析AI应用开发
  • 【OD机试】矩阵匹配
  • 【分布式锁】什么是分布式锁?分布式锁的作用?
  • redis前期工作:环境搭建-在ubuntu安装redis
  • 实验-OSPF
  • 开立医疗2026年校园招聘
  • 【论文|复现】YOLOFuse:面向多模态目标检测的双流融合框架
  • OSPF路由协议单区域
  • Selenium基础教程
  • 在Ubuntu上使用QEMU学习RISC-V程序(2)gdb调试
  • 【OpenCV篇】OpenCV——03day.图像预处理(2)
  • 征服 Linux 网络:核心服务与实战解析
  • 《从点击到共鸣:论坛前端如何用交互细节编织用户体验》
  • GISBox实操指南:如何将IFC文件高效转换为3DTiles格式‌‌
  • JVM 核心内容
  • Java并发编程第六篇(AQS设计理念与源码解析)
  • Linux724 逻辑卷挂载;挂载点扩容;逻辑卷开机自启
  • 快速启用 JMeter(macOS Automator 创建 JMeter 脚本)
  • VUE2 学习笔记5 动态绑定class、条件渲染、列表过滤与排序
  • 【AJAX】XMLHttpRequest、Promise 与 axios的关系
  • 最新免费使用Claude Code指南(Windows macOS/Linux)
  • web前端调试
  • 前端如何做安全策略
  • easyexcel流式导出
  • Windows计算器项目全流程案例:从需求到架构到实现
  • 4.5 优化器中常见的梯度下降算法
  • 绿色转向的时代红利:创新新材如何以技术与标准主导全球铝业低碳重构
  • 从手动操作到自动化:火语言 RPA 在多系统协作中的实践
  • 飞腾D3000麒麟信安系统下配置intel I210 MAC
  • 基础入门 [CMD] Windows SSH 连接服务器教程(系统自带方式)