一、共享内存的逻辑
像上图所示,当进程 P1 向其虚拟内存中的区域 1 写入数据时,进程 2 就能同时在其虚 拟内存空间的区域 2 看见这些数据,中间没有经过任何的转发,效率极高。
共享内存是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递将不再涉及到内核,换句话说,进程将不再通过执行进入系统内核的系统调用来传递彼此的数据。
二、使用共享内存的一般步骤是:
1,获取共享内存对象的 ID
注意:
(1)需创建y一个唯一的key值--->ftok();
(2)id = shmget();开辟共享内存空间,空间只能是偶数
2,将共享内存映射至本进程虚拟内存空间的某个区域
(把共享内存的空间映射进程的虚拟内存空间----->*p = shmat();
写入和读取都可以用拿到的指针p去直接修改。)
3,当不再使用时,解除映射关系
(取消映射----->shmdt(p);)
4,当没有进程再需要这块共享内存时,删除它。
(删除----->shmctl();)
三、相关API:
1.创建共享内存
功能 | 获取共享内存的 ID | ||
头文件 | #include <sys/ipc.h> #include <sys/shm.h> | ||
原型 | int shmget(key_t key, size_t size, int shmflg); | ||
参数 | key | 共享内存的键值 | |
size | 共享内存的尺寸 (PAGE_SIZE 的整数倍) | ||
shmflg | IPC_CREAT | 如果 key 对应的共享内存不存在,则创建之 | |
IPC _EXCL | 如果该 key 对应的共享内存已存在,则报错 | ||
SHM _HUGETLB | 使用“大页面”来分配共享内存 | ||
SHM _NORESERVE | 不在交换分区中为这块共享内存保留空间 | ||
mode | 共享内存的访问权限 (八进制,如 0644) | ||
返回值 | 成功 | 该共享内存的 ID | |
失败 | - 1 | ||
备注 | 如果 key 指定为为 IPC_PRIVATE,则会自动产生一个随机未用的新键值 |
2.映射或解除映射
功能 | 对共享内存进行映射,或者解除映射 | |||
头文件 | #include <sys/types.h> #include <sys/shm.h> | |||
原型 | void *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr); | |||
参数 | shmid | 共享内存 ID | ||
shmaddr | shmat( ) | 1 ,如果为 NULL,则系统会自动选择一个合适的虚拟 内存空间地址去映射共享内存。 2,如果不为 NULL,则系统会根据 shmaddr 来选择一 个合适的内存区域。 | ||
shmdt( ) | 共享内存的首地址 | |||
shmflg | SHM_RDONLY | 以只读方式映射共享内存 | ||
SHM_REMAP | 重新映射,此时 shmaddr 不能为 NULL | |||
SHM_RND | 自动选择比 shmaddr 小的最大页对齐地址 | |||
返回值 | 成功 | 共享内存的首地址 | ||
失败 | - 1 | |||
备注 | 无 |
注意:共享内存只能以只读或者可读写方式映射,无法以只写方式映射
解除映射之后,进程不能再允许访问 SHM。
3.删除映射
功能 | 获取或者设置共享内存的相关属性 | |||
头文件 | #include <sys/ipc.h> #include <sys/shm.h> | |||
原型 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); | |||
参数 | shmid | 共享内存 ID | ||
cmd | IPC_STAT | 获取属性信息,放置到buf 中 | ||
IPC_SET | 设置属性信息为 buf 指向的内容 | |||
IPC _RMID | 将共享内存标记为“即将被删除”状态 | |||
IPC _INFO | 获得关于共享内存的系统限制值信息 | |||
SHM _INFO | 获得系统为共享内存消耗的资源信息 | |||
SHM _STAT | 同 IPC_STAT,但 shmid 为该 SHM 在内核中记录所 有 SHM 信息的数组的下标,因此通过迭代所有的 下标可以获得系统中所有 SHM 的相关信息 | |||
SHM _LOCK | 禁止系统将该 SHM 交换至 swap 分区 | |||
SHM _UNLOCK | 允许系统将该 SHM 交换至 swap 分区 | |||
buf | 属性信息结构体指针 | |||
返回值 | 成功 | IPC _INFO | 内核中记录所有 SHM 信息的数组的下标最大值 | |
SHM _INFO | ||||
SHM _STAT | 下标值为 shmid 的 SHM 的 ID | |||
失败 | - 1 | |||
备注 | 无 |
代码演示:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include<fcntl.h>
#include<sys/shm.h>
#include<errno.h>
int main()
{
key_t key=ftok("./",1);
if(key<0)
{
perror("ftok err");
return -1;
}
//1.创建共享内存
int id=shmget(key,1024,IPC_CREAT|IPC_EXCL|0644);
if(id<0)
{
if(errno==EEXIST)
{
id = shmget(key,1024,0644);
}
else
{
perror("shmget err");
return -1;
}
}
//2.映射
char *p=shmat(id,NULL,0); //用shmat()映射到你的虚拟内存
if(p ==(char *)-1)
{
perror("shmat err");
return -1;
}
fgets(p,32,stdin);
printf("%s\n",p);
}
运行结果:在共享内存中输入hello world,将打印出hello world;
在虚拟机终端输入 ipcs -m;查看共享内存;
删除指定的共享内存:ipcrm -m SHM_ID 或者 ipcrm -M shm_key
取消映射和删除共享内存的代码
//取消映射
shmdt(p);
//删除共享内存
shmctl(id,IPC_RMID,NULL);