共享内存的原理
描述共享内存
共享内存就是借助同一块物理内存映射不同的虚拟地址空间,不同的虚拟地址空间代表着不同的进程,所以多个进程都能看到这一块内存,那么就可以同时对这块内存读写操作。
我们画张图感受一下:
组织共享内存
共享内存是一块内存,它的数据结构中都有以下数据
使用共享内存
相关函数
shmget函数:用来创建共享内存
int shmget(key_t key,size_t size,int shmflg);
//key共享内存段的名字
//size:共享内存大小(返回的大小等于size向上取整到4k的整数倍例如size=4097,那么返回的内存大小为8k即为两页)
//shmflg:权限
//返回值:成功返回一个非负整数(内存标识码),失败返回-1;
shmat函数:将共享内存连接到进程地址空间
void* shmat(int shmid,const void* shmaddr,int shmflg);
//shmid:共享内存标识码
//shmaddr:连接的地址
//shmflg:有两个取值可能是SHM_RND和SHM_RDONLY
//返回值:成功返回一个指针,指向共享内存第一个字节,失败返回-1
说明:
1、shmaddr为NULL,内核自动选择一个地址
2、不为NULL,如果shmflg设置了SHM_RND标记,那么连接的地址会自动向下调整为SHMLBA的整数倍(shmaddr-(shmaddr%SHMLBA);如果没有设置SHM_RND标记那么shmaddr就是连接的地址。
3、shmflg=SHM_RDONLY,表示连接操作用来只读共享内存
shmdt函数:将共享内存段与当前进程脱离
int shmdt(const void* shmaddr);
//shmaddr:是shmat返回的shmaddr
//返回值:失败返回-1,成功返回0
注意:
将共享内存与当前进程脱离并不意味着共享内存的释放。
shmctl函数:共享内存的控制函数
int shmctl(int shmid,int cmd,struct shmid_ds* uf);
//shmid:shmget函数获得的共享内存标识符
//cmd:将要采取的动作
//buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
//成功返回0,失败返回-1
cmd解释:
命令 | 说明 |
---|---|
IPC_STAT | 把shmid结构中的数据设置为共享内存的当前关联值 |
IPC_SET | 在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值 |
IPC_RMID | 删除共享内存段 |
模拟实现client-server通信
示例代码:
client.c文件:
#include"shmem.h"
#include<sys/shm.h>
int main(){
int shmid=OpenShm(4096);
sleep(1);
char* addr=shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i<20){
addr[i]='A'+i;
i++;
addr[i]=0;
sleep(1);
}
shmdt(addr);
sleep(2);
return 0;
}
server.c文件:
#include"shmem.h"
int main(){
int shmid=CreateShm(4096);
char* addr=shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i++<20){
printf("client say:%s\n",addr);
sleep(1);
}
shmdt(addr);
sleep(2);
DestroyShm(shmid);
return 0;
}
shmem.c文件:
#include"shmem.h"
static int commshm(int size,int flags){
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0){
perror("ftok\n");
return -1;
}
int shmid=0;
if(shmid=shmget(key,size,flags)<0){
perror("shmget\n");
return -1;
}
return shmid;
}
int CreateShm(int size){
return commshm(size,IPC_CREAT|IPC_EXCL|0666);
}
int OpenShm(int size){
return commshm(size,IPC_CREAT|IPC_EXCL|0666);
}
int DestroyShm(int shmid){
if(shmctl(shmid,IPC_RMID,NULL)<0){
perror("shmctl\n");
return -1;
}
}
shmem.h文件:
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 0X6666
int CreateShm(int size);
int OpenShm(int size);
int DestroyShm(int shmid);