共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
特点
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
- 因为多个进程可以同时操作,所以需要进行同步。
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
原型
#include <sys/shm.h>
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int shmflg);
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shmid, const void *shmaddr, int shmflg);
// 断开与共享内存的连接:成功返回0,失败返回-1
int shmdt(const void *shmaddr);
// 控制共享内存的相关信息:成功返回0,失败返回-1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmget函数
// 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
int shmget(key_t key, size_t size, int shmflg);
shmget
用于创建新的共享内存段或获取已存在的共享内存段,返回一个唯一的标识符,后续操作都基于这个标识符。
参数详解
key_t key
- 作用:共享内存段的键值
- 取值:
IPC_PRIVATE
:创建新的私有共享内存(通常值为0)- 自定义键值:通过
ftok()
生成或直接指定整数值
- 说明:用于标识共享内存段,不同进程使用相同key可访问同一内存
size_t size
- 作用:共享内存段的大小(字节)
- 说明
- 创建新段时:指定需要的内存大小
- 获取已有段时:可设为0(系统忽略)
- 实际分配的内存会按页大小(通常4KB)对齐
int shmflg
- 作用:标志位和权限的组合
- 常用标志:
IPC_CREAT
:如果不存在则创建IPC_EXCL
:与IPC_CREAT
一起使用,如果已存在则失败IPC_NOWAIT
:如果操作不能立即完成则报错
- 权限模式(与文件权限类似):
0666
:所有用户可读写0644
:用户可读写,组和其他用户只读
返回值
- 成功:返回共享内存标识符(正整数)
- 失败:返回-1,并设置
errno
shmat函数
// 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmat
是 System V 共享内存机制中的关键函数,用于将共享内存段附加到当前进程的地址空间,使进程能够访问和操作共享内存。
一句话理解shmat
将 shmget
创建的共享内存段映射到当前进程的虚拟地址空间,返回一个指针,进程可以通过这个指针直接读写共享内存。
参数详解
int shmid
- 作用:共享内存标识符
- 说明:由 shmget() 函数返回的标识符,指定要附加的共享内存段
const void *shmaddr - 作用:指定附加地址(通常设为NULL让系统自动选择)
- 取值:
- NULL:让系统自动选择合适地址(推荐)
- 具体地址:尝试在指定地址附加(需要对齐和权限)
int shmflg
- 作用:附加标志位
- 常用标志:
- SHM_RDONLY:以只读方式附加
- SHM_RND:与 shmaddr 配合使用, Round down address
- 0:默认读写方式
返回值
- 成功:返回共享内存段附加到进程地址空间的起始地址
- 失败:返回 (void *) -1,并设置 errno
strcpy 函数
strcpy
是 C 语言标准库中最基本且最常用的字符串操作函数之一,用于将一个字符串复制到另一个字符串。
一句话理解strcpy
将源字符串(包括结束符 \0)完整地复制到目标字符串中,覆盖目标原有的内容
原型
#include <string.h> // 必须包含的头文件
char *strcpy(char *dest, const char *src);
参数说明
char *dest
- 作用:目标字符串缓冲区
- 要求:必须有足够的空间存储源字符串及其结束符
const char *src - 作用:源字符串(不会被修改)
- 要求:必须以 \0 结尾的有效字符串
返回值 - 返回目标字符串的指针(即 dest)
- 方便链式调用,但很少使用
使用
char *shmaddr;
strcpy("shmaddr,"hello world");
shmdt函数
shmdt 是 System V 共享内存机制中的重要函数,用于将共享内存段从当前进程的地址空间中分离。它完成了共享内存使用的”善后工作”。
一句话理解
shmdt 断开进程与共享内存段的连接,使该内存区域不再对当前进程可访问,但不会删除共享内存段本身。
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数详解
const void *shmaddr
- 作用:共享内存段在当前进程中的起始地址(shmat返回的起始地址)
- 说明:必须是之前 shmat() 调用返回的有效地址
- 要求:必须指向已附加的共享内存段起始位置
返回值
- 成功:返回 0
- 失败:返回 -1,并设置 errno
shmctl函数
对已存在的共享内存段执行各种控制操作,例如查询状态、修改权限、锁定内存以及最重要的——销毁内存段。
函数原型
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数解析
int shmid
- 含义: 共享内存段的标识符。
- 来源: 这个 ID 是由之前成功调用 shmget() 函数返回的。
- 作用: 指定你要操作的是哪一个共享内存段。
int cmd - 含义: 要执行的控制命令。
- 作用: 这是函数的核心,决定了 shmctl 的行为
命令 | 说明 | 是否需要 buf |
---|---|---|
IPC_RMID | 标记删除共享内存段。这是最常用的命令。 | 否 (可设为 NULL ) |
IPC_STAT | 获取共享内存段的当前状态信息(如权限、大小、附加进程数等),并将其存入 buf 指向的结构体中。 | 是 |
IPC_SET | 根据 buf 指向的结构体中的值,来设置共享内存段的某些参数(主要是 shm_perm.uid , shm_perm.gid , shm_perm.mode )。 | 是 |
SHM_LOCK | 锁定共享内存段,防止其被交换到磁盘(swap)。(仅超级用户可用) | 否 |
SHM_UNLOCK | 解锁共享内存段。(仅超级用户可用) | 否 |
IPC_INFO / SHM_INFO | 获取系统级的共享内存限制和参数信息(更高级的用法)。 | 是 |
struct shmid_ds *buf |
- 含义: 指向
struct shmid_ds
结构体的指针。该结构体用于存放共享内存段的元数据和状态信息。 - 作用: 根据
cmd
的不同,这个参数有两种用途:- 输入: 当
cmd
为IPC_SET
时,buf
提供了要设置的新值。 - 输出: 当
cmd
为IPC_STAT
时,函数会将共享内存段的信息填充到这个结构体中。
- 输入: 当
struct shmid_ds
结构体定义(简化):
struct shmid_ds {
struct ipc_perm shm_perm; /* 所有权和权限信息 */
size_t shm_segsz; /* 段的大小(字节) */
time_t shm_atime; /* 最后一次附加时间 */
time_t shm_dtime; /* 最后一次分离时间 */
time_t shm_ctime; /* 最后一次改变时间 */
pid_t shm_cpid; /* 创建者的PID */
pid_t shm_lpid; /* 最后操作者的PID */
shmatt_t shm_nattch; /* 当前附加进程的数量 */
...
};
其中的 struct ipc_perm
包含了权限信息:
struct ipc_perm {
key_t __key; /* 创建时提供的key */
uid_t uid; /* 所有者的有效UID */
gid_t gid; /* 所有者的有效GID */
uid_t cuid; /* 创建者的有效UID */
gid_t cgid; /* 创建者的有效GID */
unsigned short mode; /* 权限位 (RWX) */
unsigned short __seq; /* 序列号 */
};
返回值
- 成功: 返回
0
。 - 失败: 返回
-1
,并设置全局变量errno
以指示错误类型(例如EINVAL
无效的shmid
,EPERM
权限不足等)。
代码
写的代码 shmw.c
#include <sys/shm.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// int shmget(key_t key, size_t size, int shmflg);
// void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);
// int shmdt(const void *shmaddr);
int main()
{
int shmid;//用来存储共享内存的id
char *shmaddr;//shmat返回的起始地址
key_t key;
key = ftok(".",1);//创建索引值
shmid = shmget(key,1024 * 4,IPC_CREAT|0666);//创建共享内存空间
if(shmid == -1)
{
printf("shmget noOK\n");
exit(-1);
}
shmaddr = shmat(shmid,NULL,0);//挂载
printf("shmat ok\n");
strcpy(shmaddr,"hello world");//将字符串内容写进shmaddr里面
sleep(5);
shmdt(shmaddr);//断开连接
shmctl(shmid,IPC_RMID,0);//删除内存
printf("quit\n");
return 0;
}
读的代码 shmr.c
#include <sys/shm.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// int shmget(key_t key, size_t size, int shmflg);
// void *shmat(int shmid, const void *_Nullable shmaddr, int shmflg);
// int shmdt(const void *shmaddr);
int main()
{
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1);
shmid = shmget(key,1024 * 4,0);//获取共享内存不创建
if(shmid == -1)
{
printf("shmget noOK\n");
exit(-1);
}
shmaddr = shmat(shmid,NULL,0);//挂载
printf("shmat ok\n");
printf("data: %s\n",shmaddr);
shmdt(shmaddr);
printf("quit\n");
return 0;
}
重点
写入
- 索引值->创建共享内存->挂载-> 写入内容->等待读取->断开内存连接->删除内存
读取 - 索引值->获取共享内存不创建->挂载-> 打印内容->断开内存连接