目录
[System V 原理](#System V 原理)
[1 创建 System V共享内存](#1 创建 System V共享内存)
[1.1 ftok系统调用](#1.1 ftok系统调用)
[1.2 shmget系统调用](#1.2 shmget系统调用)
[1.2.1 key](#1.2.1 key)
[1.2.2 size](#1.2.2 size)
[1.2.2 shmflg](#1.2.2 shmflg)
[2 删除 System V 共享内存](#2 删除 System V 共享内存)
[2.1 指令删除](#2.1 指令删除)
[2.2 系统调用删除](#2.2 系统调用删除)
[2.2.1 shmctr](#2.2.1 shmctr)
[3 关联 System V 共享内存](#3 关联 System V 共享内存)
[3.1 shmat](#3.1 shmat)
[3.2 shmdt](#3.2 shmdt)
[4 使用 System V 共享内存](#4 使用 System V 共享内存)
System V 原理

1 创建 System V共享内存
1.1 ftok系统调用
(1)功能
ftok(File to Key) 是 System V IPC(进程间通信)中的一个关键函数 ,用于 将一个文件路径和一个项目标识符(proj_id)转换为唯一的 key_t 键值,供 shmget(共享内存)、msgget(消息队列)或 semget(信号量)使用。
ftok 通过 文件路径的 inode 号 和 proj_id计算出一个唯一的 IPC 键值,确保:
- 不同进程 使用相同的 pathname 和 proj_id 时,得到相同的 key_t,从而访问同一个 IPC 资源(如共享内存)。
- 不同文件 或 不同 proj_id 会生成不同的 key_t,避免冲突。
(2)函数原型
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
(3)参数
- pathname :一个已存在的文件路径(如 /tmp/myfile 或 /home/user/config)。理论上来说是可以随便乱填的
- proj_id :项目标识符(1 字节,范围 0-255,通常用字符 ASCII 码,如 'A')。理论上来说是可以随便乱填的
(4)返回值
- 成功:返回生成的 key_t 键值(用于 IPC 操作)。
- 失败:返回 -1,并设置 errno(如文件不存在时 ENOENT)。
1.2 shmget系统调用
(1)功能
shmget(Shared Memory GET) 是 Linux 用于创建或获取共享内存段 的系统调用,属于 System V IPC(进程间通信)机制之一。它允许不同进程访问同一块物理内存,实现高效数据共享
(2)函数原型
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
(3)参数
- key : 共享内存的唯一标识(IPC_PRIVATE 或 ftok() 生成的 key)
- size : 共享内存大小(字节),如果是获取已有内存,可设为 0
- shmflg : 权限标志(如 IPC_CREAT、IPC_EXCL)和权限模式(如 0666)的组合
(4)返回值
- 成功:返回共享内存标识符(shmid,非负整数)
- 失败:返回 -1,并设置 errno(如 EEXIST、ENOENT)
1.2.1 key
理论上是可以随便填写一个整数,但是这样会增加 shmget 内部冲突的概率,因为随便填的整数我们不会一直记得
(1)key的生成方式
-
IPC_PRIVATE创建一个仅当前进程及其子进程可用的私有共享内存(其他进程无法访问)。
int shmid = shmget(IPC_PRIVATE, size, 0666);
-
ftok() 生成唯一 key通过文件路径和项目 ID 生成 key,确保不同进程使用相同的 key 访问同一内存。
key_t key = ftok("/tmp/myfile", 'A'); // 文件必须存在
int shmid = shmget(key, size, 0666 | IPC_CREAT);
1.2.2 size
共享内存的大小最好为 4096 的整数倍
因为操作系统会在内核层面对共享内存的大小与 4096字节 进行对齐,比如申请的大小为4097字节,操作系统为了对4096字节进行对齐,实际申请的大小为8192字节,因此就浪费了4095个字节大小的空间
1.2.2 shmflg
-
IPC_CREAT单独:如果创建的共享内存不存在,就创建相应的共享内存。如果创建的共享内存已经存在,直接获取它
-
IPC_EXCL:不能单独作为一个参数使用,需要配合 IPC_CREAT 使用
-
IPC_CREAT | IPC_EXCL:如果创建的共享内存不存在,就创建相应的共享内存。如果创建的共享内存已经存在,直接出错返回,设置出错码
-
0666 : 权限设置(所有者、组、其他用户可读写)
int shmid = shmget(key, 1024, 0666 | IPC_CREAT); // 创建 1KB 共享内存
int shmid = shmget(key, 0, 0666); // size=0 表示只获取,不创建
int shmid = shmget(IPC_PRIVATE, 1024, 0666); // 私有共享内存(仅父子进程可用)
2 删除 System V 共享内存
(1)查看当前系统创建的所有 System V 共享内存
ipcs -m
lz@VM-8-15-ubuntu:~/VSCode/demo$ ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
- key : 只有内核使用,用来标识 shm 的唯一性
- shmid: 给用户使用,用来进行 shm 访问
- owner: 共享内存的创建者
- perms:
- bytes: 共享内存的大小
- nattch: 当前共享内存有多少个进程使用
- status:
2.1 指令删除
ipcrm -m [shmid]
注意 :使用命令行进行共享内存的删除需要使用共享内存的 shmid 值进行删除,不可以使用 key 进行删除
2.2 系统调用删除
struct shmid_ds
{
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment(bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of currentattaches */
unsigned short shm_unused; /* compatibility */
void shm_unused2; / ditto - used by DIPC */
void shm_unused3; / unused */
};
2.2.1 shmctr
(1)功能
**shmctl()**是System V共享内存的核心控制函数,用于对共享内存段执行各种控制操作。它的功能类似于文件系统的ioctl(),提供了对共享内存段的管理接口
(2)函数原型
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
(3)参数
1.shmid
- 共享内存段的标识符,由**shmget()**返回
2.cmd - 控制命令,指定要执行的操作类型:
- IPC_STAT :获取共享内存段的shmid_ds结构
- IPC_SET:设置共享内存段的参数
- IPC_RMID:标记删除共享内存段
- IPC_INFO /SHM_INFO /SHM_STAT等(Linux特有)
3.buf - 指向shmid_ds结构的指针,用于读取或设置共享内存段信息
- 对于某些命令可以为NULL
(4)返回值
- 成功 :根据cmd不同返回不同值
- IPC_STAT /IPC_SET /IPC_RMID:返回0
- IPC_INFO /SHM_INFO:返回内核内部数组的最大使用索引
- SHM_STAT:返回共享内存标识符
- 失败:返回-1并设置errno
3 关联 System V 共享内存
3.1 shmat
(1)功能
**shmat()**是System V共享内存机制中的关键函数,用于将共享内存段附加(attach)到调用进程的地址空间(通过页表映射到自己的虚拟地址空间中),使进程能够访问共享内存区域。
(2)函数原型
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
(3)参数
1.shmid
- 共享内存段的标识符,由**shmget()**调用返回
2.shmaddr - 指定共享内存附加到进程地址空间的哪个位置:
- 如果为NULL,系统自动选择合适地址
- 如果非NULL且未指定SHM_RND,则附加到指定地址
- 如果非NULL且指定了SHM_RND ,地址会向下对齐到SHMLBA(Shared Memory Low Boundary Address)的倍数
3.shmflg - 附加标志位,可以是以下值的按位或:
- SHM_RDONLY:以只读方式附加
- SHM_REMAP:(Linux特有)替换shmaddr处的现有映射
- SHM_RND:与shmaddr配合使用,表示地址需要舍入
- SHM_EXEC:(Linux特有)允许执行共享内存中的内容
- 0 :使用系统缺省值
(4)返回值
- 成功:返回附加的共享内存段在进程地址空间中的起始地址(类似于 malloc 函数返回值)
- 失败:返回(void *)-1并设置errno
3.2 shmdt
(1)功能
System V共享内存机制中用于将虚拟地址空间中共享内存段从进程地址空间分离(detach)的函数
(2)函数原型
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
(3)参数
- shmaddr 要分离的共享内存段的起始地址,必须是之前**shmat()**调用返回的地址值
(4)返回值
- 成功:返回0
- 失败:返回-1并设置errno
4 使用 System V 共享内存
Linux系统System V共享内存的学习和使用,实现一个简单的客户端和服务端的进程通信 · f1b3a58 · lv-zhuo/AsCent - Gitee.com