目录
共享内存的原理
共享内存通信的实现步骤
通信实例
共享内存的原理
原理:可以说,共享内存是一种最为高效的进程间通信方式。因为进程可以直接读写内存,不需要任何数据的复制。为了在多个进程间交换信息,内核专门留出一块内存区。这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。因此,进程就可以直接读写这一内存区而不需要进行数据的复制,从而大大提高了效率。当然,由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。其原理示意图如下:
总结:共享内存的通信原理就是让不同的进程看到同一份内存。
共享内存通信的实现步骤:
1、创建key,通过调用ftok()生成标识共享内存的key值。
2、创建共享内存,使用shmget()向OS申请一段内存。
3、将共享内存和要进行通信的进程关联。
4、进行数据通信和通信数据的处理。
5、通信完成后,将所有进程和共享内存去关联。
6、删除共享内存。
注意:共享内存的创建只需一个进程进行即可,其余要进行通信的进程,则需要获取共享内存。
共享内存的创建
调用shmget( )创建共享内存
函数原型:
int shmget(key_t key, size_t size, int shmflg);
功能:用来创建共享内存。
参数
key:这个共享内存段名字。
size:共享内存大小。
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。
关于shmget( )的第一个参数key,是用于标识共享内存的唯一性,通过调用ftok( )使用一定的算法生成的。
ftok()原型:
key_t ftok(const char *pathname, int proj_id);
参数说明:
pathname:一个已存在的路径。
proj_id:项目id。
函数功能:将一个已存在的路径和项目id转换为一个key值。
shmget( )的第三个参数shmflg创建共享内存的方式,常见的是用方式:
单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回。IPC_EXCL不能单独使用,一般都要配合IPC_CREAT。
IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!
这里我们先记住在多个进程要使用同一个共享内存时仅需一个进程使用IPC_CREAT | IPC_EXCL组合,其余进程单独使用IPC_CREAT。
进程和共享内存关联
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
通信实例:
server.cc
#include"comm.hpp"
#include<unistd.h>int main(){//1、创建key
key_t k =getKey();
cout<<"server key:"<<toHex(k)<<endl;//2、创建共享内存
int shmid=createShm(k,gsize);
cout<<"server shmid: "<<shmid<<endl;
sleep(3);//3、将当前进程和共享内存内存关联
char* start=attachShm(shmid);
//sleep(20);//4、进行通信
//char* start=attachShm(shmid);//init.getStart();
int n=0;
while(n<=30)
{cout<<"client -> server# "<<start<<endl;sleep(1);n++;
}//5、通信完成 将当前进程和共享内存去关联
detachShm(start);//删除共享内存delShm(shmid);return 0;}
client.cc
#include"comm.hpp"
#include<unistd.h>
int main()
{//1、获取共享内存
key_t k=getKey();
cout<<"client key: "<<toHex(k)<<endl;int shmid=getShm(k,gsize);
cout<<"client shmid: "<<shmid<<endl;//2、将当前进程和共享内存关联
char* start=attachShm(shmid);
//3、进行通信
//char* start=(char*)attachShm(shmid);//init.getStart();
char c='A';
while(c<='Z')
{start[c-'A']=c;c++;start[c-'A']='\0';sleep(1);
}
//sleep(15);//4、将当前进程和共享内存去关联
detachShm(start);
return 0;}
comm.hpp
#ifndef __COMM_HPP__
#define __COMM_HPP__#include<iostream>
#include<cerrno>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/stat.h>using namespace std;
#define PATHNAME "."
#define PROJID 0X6666const int gsize =4096;key_t getKey()
{key_t k =ftok(PATHNAME,0X6666);if(k==-1){cerr<<"error: "<<errno<<" : "<<strerror(errno)<<endl;exit(1);}return k;
}
string toHex(int x)
{char buffer[64];snprintf(buffer,sizeof buffer,"0x%x",x);return buffer;
}static int createShmHelper(key_t k,int gsize,int flog)
{int shmid=shmget(k,gsize,flog);if(shmid==-1){cerr<<"cerror: "<<errno<<" : "<<strerror(errno)<<endl;exit(2);}return shmid;
}int createShm(key_t k,int size)
{umask(0);return createShmHelper(k,size,IPC_CREAT|IPC_EXCL|0666);
}int getShm(key_t k,int size)
{return createShmHelper(k,size,IPC_CREAT);
}char* attachShm(int shmid)
{char*start=(char*)shmat(shmid,nullptr,0);return start;
}
void detachShm(char*start)
{int n=shmdt(start);assert(n!=-1);(void)n;
}void delShm(int shmid)
{int n=shmctl(shmid,IPC_RMID,nullptr);assert(n!=-1);(void)n;
}#endif
运行结果: