共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
共享内存原理
- 在物理内存中开辟一块内存空间
- 将这块内存空间通过页表映射到进程的虚拟地址空间中
- 进程可以直接通过进程虚拟地址空间访问这块物理内存,进行操作(若多个进程映射同-块物理内存,就可以实现相互通信-直接通过虚拟地址空间改变内存中的数据,其他进程也随之改变,相较于其他进程通信方式少了两步内核态与用户态之间数据拷贝过程,因此速度最快)
- 解除映射关系
- 删除共享内存
共享内存函数
shmget函数
- 功能:用来创建共享内存
- 原型
int shmget(key_t key, size_t size, int shmflg); - 参数
- key:这个共享内存段名字
- size:共享内存大小
- shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
例:IPC_CREAT | 0664
- 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
shmat函数
- 功能:将共享内存段连接到进程地址空间
- 原型
void *shmat(int shmid, const void *shmaddr, int shmflg); - 参数
- shmid: 共享内存标识
- shmaddr:指定连接的地址
- shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
- 返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1
- 说明
- shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
shmdt函数
- 功能:将共享内存段与当前进程脱离
- 原型
int shmdt(const void *shmaddr); - 参数
shmaddr: 由shmat所返回的指针 - 返回值:成功返回0;失败返回-1
- 注意:将共享内存段与当前进程脱离不等于删除共享内存段
shmctl函数
- 功能:用于控制共享内存
- 原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf); - 参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构 - 返回值:成功返回0;失败返回-1
共享内存删除流程:
首先判断当前映射链接数是否为0,若为0则直接删除;否则表示现在还有其他进程正在使用,则共享内存不能被立即删除,但是会拒绝后续进程的映射链接,等待映射链接数为0时删除
特性:
1.最快的进程通信方式
2.生命周期随内核
注:共享内存的操作是不安全的(并不会自动具备同步与互斥关系,需要操作用户进行控制)
代码示例:
“shm_write.c”
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main() {
//1.物理开辟一块内存空间
int shmid = shmget(IPC_KEY, 32, IPC_CREAT | 0664);
if (shmid < 0) {
perror("shmget error");
return -1;
}
//2.将共享内存通过页表映射到进程的虚拟地址空间中
void* shm_start = shmat(shmid, NULL, 0);
if (shm_start == (void*)(-1)) {
perror("shmat error");
return -1;
}
//3.读取这块共享内存
int i = 0;
while (1) {
sprintf(shm_start, "%s-%d", "hello world", i++);
sleep(1);
}
//4.解除映射关系
shmdt(shm_start);
//5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
“shm_read.c”
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
#define IPC_KEY 0x12345678
int main() {
//1.物理开辟一块内存空间
int shmid = shmget(IPC_KEY, 32, IPC_CREAT | 0664);
if (shmid < 0) {
perror("shmget error");
return -1;
}
//2.将共享内存通过页表映射到进程的虚拟地址空间中
void* shm_start = shmat(shmid, NULL, 0);
if (shm_start == (void*)(-1)) {
perror("shmat error");
return -1;
}
//3.读取这块共享内存
while (1) {
//如果写程序停止了,则将打印最后写入共享内存的数据(注意点)
printf("%s\n", shm_start);
sleep(1);
}
//4.解除映射关系
shmdt(shm_start);
//5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}