今天我为大家讲的是进程间通信的第二种方法——共享内存。首先我们得理解一下什么是共享内存以及共享内存通信的原理。
共享内存其实就是os在内存上面开辟的一段空间,让不同的进程通过虚拟地址进程空间一起看到这同一块内存,以下是共享内存原理示意图:
创建一个共享内存:
参数key
是内核层标定共享内存唯一性的,因为os创建了许多共享内存,为了能让不同的进程找到同一个共享内存,就用key来标识。因为有很多的共享内存需要被管理,所以os也是采用了先描述后组织的方法,通过数据结构(数组)把共享内存管理起来。所以该函数的
返回值
就是用户层标识共享内存的,相当于文件描述符。
参数size
创建共享内存的大小。
参数shmflg
是创建模式:
IPC_CREAT:如果共享内存不存在就创建,存在就获取共享内存。
IPC_EXCL:1.它不能够被单独使用。2.如果不存在,创建之。如果不存在,就返回报错。
所以IPC_EXCL能够保证创建出来的共享内存是
新创建
的!!
该函数的返回值:
创建一个参数key:
key是为了标识共享内存唯一性的,所以在调用shmget系统调用的时候,需要将key传进去。其实可以告诉大家的是共享内存的内核数据结构里面就存有了可key,所以说key相当于共享内存的一种属性。以下是创建key的函数:
它的返回值:
共享内存创建好了并且两个进程都找到了同一块时,就需要将共享内存的起始地址写到进程的虚拟地址空间里面。
进程与共享内存的挂接:
它的返回值:
如果成功了,返回的是共享内存的起始地址,如果失败了返回-1.
取消挂接:
int shmdt(const void *shmaddr);
接口很简单,只需要将共享内存的起始地址填入就行。
查看共享内存接口:
ipcs -m
删除共享内存接口:
ipcrm -m + shmid
shmct函数 :
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:
由
shmget
返回的共享内存标识码
cmd:
将要采取的动作(有三个可取值)
buf:
指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回
0
;失败返回
-1
了解完了上面所有函数接口,现在我们实现一个用共享内存实现进程间通信的代码:
com.hpp:
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
using namespace std;
#define PATH_NAME "/tmp"
#define PROJ_ID 0X123
#define MAX_SIZE 4096
key_t getkey()
{
key_t key =ftok(PATH_NAME,PROJ_ID);
if(key ==-1)
{
cerr<<"getkey:"<<errno<<strerror(errno)<<endl;
exit(3);
}
return key;
}
int getShmHelper(key_t key, int flags)
{
int shmid =shmget(key,MAX_SIZE,flags);
if(shmid ==-1)
{
cerr<<"shmget:"<<errno<<strerror(errno)<<endl;
exit(-1);
}
return shmid;
}
int getShm(key_t k)
{
return getShmHelper(k, IPC_CREAT);
}
int createShm(key_t k)
{
return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);
}
void* AttachShm(int shmid)
{
void* mem =shmat(shmid,nullptr,0);
if((long long)mem ==-1)
{
cerr<<"shmat:"<<errno<<strerror(errno)<<endl;
exit(-1);
}
return mem;
}
void DetachShm(void* start)
{
if(shmdt(start)==-1)
{
cerr<<"shmdt:"<<errno<<strerror(errno)<<endl;
exit(3);
}
}
void delShm(int shmid)
{
if(shmctl(shmid, IPC_RMID, nullptr) == -1)
{
std::cerr << errno << " : " << strerror(errno) << std::endl;
}
}
client.cc:
#include "com.hpp"
int main()
{
//创建和服务端相同的key值
key_t key =getkey();
//获取共享内存
int shmid =getShm(key);
printf("shmid: %d\n", shmid);
//将客户端和进程挂接起来
char* start =(char*)AttachShm(shmid);
cout<<"client 挂接成功 address: "<<endl;
//客户端写数据
char buffer[1024]={0};
while(true)
{
cout<<"####输入数据:";
fgets(buffer,sizeof(buffer),stdin);
if(strlen(buffer)>0)
{
buffer[strlen(buffer)-1]=0;
}
int cnt =1;
char buffer1[1024];
snprintf(buffer1,sizeof(buffer1),"我是客户端,我正在给你发消息[%d][%d]:%s",getpid(),cnt++,buffer);
memcpy(start,buffer1,strlen(buffer1));
}
//客户端解除挂接
DetachShm(start);
return 0;
}
server.cc:
#include "com.hpp"
int main()
{
//获取一个key
key_t key =getkey();
//创建共享内存
int shmid =createShm(key);
//将进程和共享内存进行挂接
char* start =(char*)AttachShm(shmid);
printf("server 挂接成功 ,address: %p",start);
//服务端读取数据。
while(true)
{
cout<<"client say: "<<start<<endl;
sleep(2);
}
//解除挂接
sleep(5);
DetachShm(start);
//删除共享内存
sleep(3);
delShm(shmid);
return 0;
}
最后我们来谈一谈共享内存的
优缺点
:
优先:
共享内存是进程间通信最快的一种方式,大大减少了拷贝。
缺点:
不给我进行同步与互斥的操作,对数据没有做任何保护。
共享内存相关知识就全部介绍清楚了,感谢大家多多支持!!