Linux--进程通讯-- system V 共享内存
2026/7/26 6:15:14 网站建设 项目流程

一、什么是共享内存

共享内存是最快的 IPC 形式。一旦共享内存映射到进程的地址空间,数据传递不再涉及内核,进程无需通过系
统调用即可直接读写内存。
特点:

  • 最快:无内核介入,直接访问
  • 需要同步:必须配合信号量使用,否则会有数据竞争
  • 内核持久:共享内存不会随进程退出而自动销毁,必须手动删除。需要显式删除,命令行使用 ipcrm,代码中使用 shmctl IPC_RMID。
    注:System V 接口较复杂,新项目推荐使用 POSIX 共享内存。

二、共享内存示意图


管道、消息队列,数据都需要经过内核拷贝。共享内存,是最快的 IPC 方式,因为它不经过内核。
进程 A 有一个虚拟地址(比如 0x7f…),通过它的页表,指向了物理内存中的某个位置。进程 B 也有一个虚拟地址,通过它的页表,指向了同一块物理内存。
进程 A 和进程 B 读写的是同一块物理内存,整个过程没有经过内核拷贝。A 写完后,B 直接就能看到,不需要调用 read 或 write 系统调用。

三、共享内存数据结构

structshmid_ds{structipc_permshm_perm;intshm_segsz;__kernel_time_t shm_atime;__kernel_time_t shm_dtime;__kernel_time_t shm_ctime;__kernel_ipc_pid_t shm_cpid;__kernel_ipc_pid_t shm_lpid;unsignedshortshm_nattch;unsignedshortshm_unused;void*shm_unused2;void*shm_unused3;};


核心要点:

  • 权限由 shm_perm 控制
  • 大小 shm_segsz 创建后固定
  • shm_nattch 为0时才能安全删除共享内存
  • 时间戳和PID字段记录操作历史

四、共享内存函数

  1. shmget
#include<sys/ipc.h>#include<sys/shm.h>intshmget(key_tkey,size_tsize,intshmflg);
  • 参数:
  • key :共享内存键值( ftok() 生成 或 IPC_PRIVATE )
  • size :共享内存大小(字节)
  • shmflg : IPC_CREAT (创建)、 IPC_EXCL (确保新建)、权限位(如 0644 )
  • 返回值:成功返回 shmid ,失败返回 -1
  • 示例:
intshmid=shmget(IPC_PRIVATE,4096,IPC_CREAT|0644);
  1. shmat
#include<sys/types.h>#include<sys/shm.h>void*shmat(intshmid,constvoid*shmaddr,intshmflg);
  • 参数:
  • shmid : shmget 返回的标识符
  • shmaddr : NULL (系统自动选地址) 或 指定地址
  • shmflg : SHM_RDONLY (只读)、 SHM_RND (地址对齐)
  • 返回值:成功返回共享内存首地址指针,失败返回 (void *)-1
  • 连接地址规则
    1.shmaddr 为 NULL:系统自动选择连接地址
    2.shmaddr 非 NULL 且无 SHM_RND:使用指定地址
    3.shmaddr 非 NULL 且有 SHM_RND:地址自动向下对齐到 SHMLBA 的整数倍
  • 示例:
char*addr=(char*)shmat(shmid,NULL,0);
  1. shmdt
#include<sys/types.h>#include<sys/shm.h>intshmdt(constvoid*shmaddr);
  • 参数: shmat 返回的地址指针
  • 返回值:成功返回 0 ,失败返回 -1
  • 注意:分离≠删除,进程终止会自动分离
  1. shmctl
#include<sys/ipc.h>#include<sys/shm.h>intshmctl(intshmid,intcmd,structshmid_ds*buf);
  • 参数:

  • shmid :共享内存标识符

  • cmd : IPC_STAT (获取状态)、 IPC_SET (设置属性)、 IPC_RMID (标记删除)

  • buf : shmid_ds 结构体指针

  • 返回值:成功返回 0 ,失败返回 -1

  • 示例:

shmctl(shmid,IPC_RMID,NULL);// 删除共享内存

五、共享内存与信号量配合使用

  1. 为什么需要信号量
    共享内存本身不提供任何同步机制,如果多个进程同时读写同一块共享内存,会导致数据竞争。例如,进程 A 正在写入共享内存但只写了一半,进程 B 同时读取,就会读到不完整的数据。解决方案是使用信号量实现互斥访问,保证同一时刻只有一个进程读写共享内存。
  2. 信号量类型选择
    与 System V 共享内存配合使用时,推荐使用 POSIX 命名信号量。
    命名信号量通过 sem_open 创建和打开,使用 sem_close 关闭,sem_unlink 删除,适用于无亲缘关系的进程间同步。
    无名信号量则通过 sem_init 初始化sem_destroy 销毁,主要用于线程间或有亲缘
    关系的进程。
  3. 核心操作
  • 创建或打开信号量使用 sem_open;
  • 加锁使用 sem_wait(P 操作);
  • 解锁使用 sem_post(V 操作);
  • 关闭信号量使用 sem_close;
    最后用 sem_unlink 删除信号量。
  1. 使用流程

  2. 注意事项

  • 信号量初始值必须设为 1 才能实现互斥锁的效果。
  • P 操作必须在访问共享内存之前调用,V 操作必须在访问共享内存之后调用,并且要确保解锁操作会被执行以防止死锁。
  • 最后一个使用共享内存和信号量的进程负责删除它们。
  • 编译时需要链接 -lrt 和 -lpthread 库。注意以下常见错误:
  • 忘记加锁会导致数据竞争,读到不完整的数据;
  • 忘记解锁会导致死锁,其他进程永远等待;
  • 信号量未初始化会导致未定义行为;
  • 未处理异常退出会导致信号量未被释放,造成死锁。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询