一、什么是共享内存
共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache)。任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则不同的处理器可能用到不同的数据。共享内存是 Linux下的多进程之间的通信方法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。
二、共享内存的原理
三、共享内存的特点
在匿名管道、命名管道、消息队列、信号量、共享内存这五种进程间通信的方式中,前两种生命周期随进程,后三种生命周期随内核。而共享内存是这五种方式中效率最高的。虽然共享内存提供了进程间通信的方式,但是他没有相应的互斥机制,所以一般共享内存和信号量配合起来使用。
四、共享内存的实现原理
在实现之前,我们先来看几个共享内存的函数
1、shmget获得或者创建一个共享内存
int shmget(key_t key,size_t size,int flags);
返回值:失败返回-1,成功返回共享内存的id。
size:表示要申请的共享内存的大小,一般是4k的整数倍。
flags:IPC_CREAT和IPC_EXCL一起使用,没有就创建一个新的关系内存,否则返回-1。IPC_CREAT单独使用时返回一个共享内存,有就直接返回,没有就创建。
2、shmat将申请的共享内存挂接在该进程的页表上,是的虚拟内存和物理内存向对应。
void *shmat(int shmid);
返回值:返回这块内存的虚拟地址。
3、shmdt去挂接,将这块共享内存从页表上剥离下来。
int shmdt(const void*shmaddr);
返回值:失败返回-1.
shmaddr:表示这块物理内存的虚拟地址。
4、shmctl用来设置共享内存的属性。当cmd是IPC_RMID时可以用来删除一块共享内存。
int shmctl(int shmid,int cmd,const void* addr);
了解这几个函数之后,我们就可以来完成共享内存的实现了
/*Makefile*/
/*************************************/
.PHONY:all
all:server client
server:server.c comm.c
gcc -o $@ $^
client:client.c comm.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f server client
/*************************************/
/*comm.h*/
/*************************************/
/*************************************************************************
> File Name: comm.c
> Author:Lord'WingF'
> Mail:暂无
> Created Time: Sun 04 Jun 2017 02:08:23 AM PDT
************************************************************************/
#ifndef __COMM_H__
#define __COMM_H__
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#define PATHNAME "."
#define PROJ_ID 7777
#define SIZE 4096*1
int CreateShm();
int GetShm();
char *AtShm(int shmid);
int DtShm(const void* shmadd);
int Destoryshm(int shmid);
#endif
/*************************************/
/*comm.c*/
/*************************************/
/*************************************************************************
> File Name: comm.c
> Author:Lord'WingF'
> Mail:暂无
> Created Time: Sun 04 Jun 2017 02:08:23 AM PDT
************************************************************************/
#include"comm.h"
/*****************************************/
int CommShm(int flags)
{
key_t _key = ftok(PATHNAME, PROJ_ID);
if(_key < 0){
perror("ftok");
return -1;
}
int shmid = shmget(_key, SIZE, flags);
if(shmid < 0){
perror("shmget");
return -2;
}
return shmid;
}
/*****************************************/
/*****************************************/
int CreateShm()
{
return CommShm(IPC_CREAT | IPC_EXCL | 0666);
}
/*****************************************/
/*****************************************/
int GetShm()
{
return CommShm(IPC_CREAT);
}
/*****************************************/
/*****************************************/
char *AtShm(int shmid)
{
char* addr=shmat(shmid,NULL,0);
return (char*)addr;
}
/*****************************************/
/*****************************************/
int DtShm(const void* shmaddr)
{
return shmdt(shmaddr);
}
/*****************************************/
/*****************************************/
int DestoryShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
return -1;
}
return 0;
}
/******************************************/
/*************************************/
/*server.c*/
/*************************************/
/*************************************************************************
> File Name: server.c
> Author:LordWingF
> Mail: NULL
> Created Time: Sun 11 Jun 2017 02:10:42 AM PDT
************************************************************************/
#include"comm.h"
int main()
{
int shmid=CreateShm();
char* buf=(char*)AtShm(shmid);
int i=0;
while(i<(SIZE-1))
{
buf[i]='A';
i++;
}
buf[SIZE-1]='\0';
DtShm(buf);
DestoryShm(shmid);
return 0;
}
/**************************************/
/*client.c*/
/**************************************/
/*************************************************************************
> File Name: client.c
> Author:LordWingF
> Mail: NULL
> Created Time: Sun 11 Jun 2017 02:10:53 AM PDT
************************************************************************/
#include"comm.h"
int main()
{
int shmid=GetShm();
char* buf=(char*)AtShm(shmid);
printf("%s\n",buf);
DtShm(buf);
return 0;
}
/**************************************/
程序结果如下
调用ipcs -m来查看创建的共享内存
为了确保能够一直使用我们调用ipcrm -m +shmid来释放这个共享内存
这就是共享内存间内存的简单通讯
五、共享内存效率高的原因
共享内存区是最快的可用IPC形式,一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就不再通过执行任何进入内核的系统调用来传递彼此的数据,节省了时间。
共享内存和消息队列,FIFO,管道传递消息的区别:
后者,消息队列,FIFO,管道的消息传递方式一般为
1:服务器得到输入
2:通过管道,消息队列写入数据,通常需要从进程拷贝到内核。
3:客户从内核拷贝到进程
4:然后再从进程中拷贝到输出文件
上述过程通常要经过4次拷贝,才能完成文件的传递。
而共享内存只需要
1:从输入文件到共享内存区
2:从共享内存区输出到文件
上述过程不涉及到内核的拷贝,所以花的时间较少