一:共享内存
我们知道,当一个进程在运行起来的情况下,系统会为其自动创建0-4G的虚拟内存空间,根据虚拟地址和物理地址之间的映射关系,进程可以通过对虚拟内存地址的操作实现对物理页面的操作。一般情况下,每个进程的虚拟地址空间会与不同的物理地址进行映射,但是当使用共享内存进行通信时,系统会将同一段物理内存映射给不同的进程。
二:共享内存的特点
1. 共享内存的大小必须是物理页面大小的整数倍 即4k的整数倍
2. 共享内存使用前必须要和虚拟内存进行映射,映射完成之后,对于虚拟内存的操作就相当于直接对物理内存直接进行操作读写
3. 与申请堆空间类似,当通信完成之后,也应该释放物理内存,解除进程与物理内存之间的映射关系
4. 共享内存对于多个进程同一时间访问没有做限制,需要通过信号量这一机制来实现读写操作的同步
//shm_w.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/ipc.h>
#define SEGSIZE 4096 // 定义共享内存容量
typedef struct
{
char name[8];
int age;
} Stu;
int main(int argc,char* argv[]){
int shm_id, i;
key_t key;
char name[8];
Stu *smap;
key = ftok("/", 0); // 获取关键字
if (key ==-1){
perror("ftok error");
return -1;
}
//1. 创建共享内存
shm_id = shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0664);
if (shm_id ==- 1){
perror("create shared memory error\n");
return - 1;
}
printf("shm_id=%d\n", shm_id);
// NULL 表示不确定虚拟内存对应的地址 由系统决定地址
smap = (Stu*)shmat(shm_id, NULL, 0); // 将进程与共享内存绑定 smap 是shmat 函数返回的是映射的虚拟内存地址
memset(name, 0x00, sizeof(name));
strcpy(name, "lisi");
name[4]='0';
for (i=0; i<3;i++) {
name[4] += 1;
strncpy((smap+i)->name, name, 5);
(smap+i)->age = 20+i;
}
if (shmdt(smap) == -1){ // 删除绑定
perror("detach error");
return -1;
}
return 0;
}
//shm_r.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/ipc.h>
#define SEGSIZE 4096 // 定义共享内存容量
typedef struct
{
char name[8];
int age;
} Stu;
int main(int argc, char* argv[]){
int shm_id, i;
key_t key;
Stu* smap;
// shmid_ds 共享内存管理信息 共享内存属性信息的结构体
struct shmid_ds buf;
// ftok 通过“/”来获取当前路径下的key_t, key_t是用来做键值的,方便系统查找对应的内存映射
key = ftok("/", 0);
if (key ==-1){
perror("ftok error\n");
return -1;
}
printf("key=%d\n", key);
shm_id = shmget(key, 0, 0); // 创建共享内存
if (shm_id ==-1) {
perror("shmget error\n");
return -1;
}
printf("shm_id=%d\n", shm_id);
smap = (Stu*)shmat(shm_id, NULL, 0); // 将进程与共享内存绑定
for (i=0; i<3;i++){
printf("name is %s, age is %d\n", (*(smap+i)).name, (*(smap+i)).age);
}
if (shmdt(smap) == -1){
perror("detach error\n");
return - 1;
}
shmctl(shm_id, IPC_RMID, &buf); // 删除共享内存
return 0;
}
输出如下:
zfz:Demo zhangfengzhou$ gcc shm_w.c -o shm_w
zfz:Demo zhangfengzhou$ gcc shm_r.c -o shm_r
zfz:Demo zhangfengzhou$ ./shm_w
shm_id=131072
zfz:Demo zhangfengzhou$ ./shm_r
key=262146
shm_id=131072
name is lisi1, age is 20
name is lisi2, age is 21
name is lisi3, age is 22
zfz:Demo zhangfengzhou$
三:总结:
使用共享内存有三个步骤:
1. 创建或获取已经存在的共享内存 shmget()
2. 将进程和共享内存进行映射绑定 shmat()
3. 映射绑定之后,将返回的指针强转为我们需要的某种类型指针
4. 对指针进行操作,就是对物理内存进行操作
5. 使用完指针之后,需要解除映射关系 shmdt()
6. 对于不再使用的共享内存,要即时删除共享内存 shmctl()