目录
内存共享是进程之间最快的通信方式,为什么呢?
例:管道通信,都是以文件为媒介,需要使用系统调用将数据传入给内核,而共享内存直接与内存进行交互。
进程间通信的本质就是让进程看到同一块系统提供的资源,共享内存就是让不同进程看到同一块内存,用内存作为媒介,让两个不同进程通信。
示意图
共享内存系统调用接口
1.shmget
key:就是一个关键值,让不同的进程,通过一个key值找到同一个共享内存。
size:就是开辟多大的共享内存,共享内存的开辟是以4kb为单位的,size的大小建议设置4*n,因为即使size为7,还是会开辟8个字节的内存,剩下的一个字节就浪费了。
shmlg:想要怎么创建共享内存
IPC_CREAT:如果key对应的共享内存不存在就创建,存在就获取。
IPC_EXCL:如果共享内存存在,就出错返回,单独使用没有意义,要配合IPC_CREAT。
返回值:成功共享内存的标识符被返回,失败-1被返回,错误码被设置去指示错误。
2.ftok
ftok使用参数pathname和最少8个有效位的参数proj_id(必须不为0)生成一个key_t类型的System V IPC密钥,适用于msgget,semget,shmget这三个函数。
pathname:指定一个路径
proj_id:8个有效位的参数
成功生成key_t类型的密钥被返回,失败-1被返回,错误码被设置。
3.shmat&&shmdt
创建共享内存之后,需要shmat挂接到进程中,本质就是,建立页表的映射,映射到共享区进程就可以使用了。
使用完需要用shmdt取消挂接参数shmaddr是shmat的返回值。
shmid:是共享内存的唯一标识符。
shmaddr:是链接共享区的地址,不设置自动连接。
shmflg:
返回值
成功返回共享内存的地址,失败返回-1,错误码被设置。
4.shmctl
控制共享内存
shmid:shmget的返回值,共享内存的唯一标识。
cmd:控制共享内存的命令
IPC_STAR:将shmid_ds结构中的数据设置为当前共享内存的相关值。
IPC_SET:在进程有足够权限下,将共享内存的值设置为shmid_ds的值。
IPC_RMID:删除共享内存
使用共享内存进行通信
//clint.cc
#include"comm.hpp"
using namespace std;
int main()
{
key_t key = getkey();//生成密钥
cout<<"create key"<<endl;
int shmid = getShm(key);//创建共享内存
cout<<"get shm"<<endl;
char * adress = (char*)shmatch(shmid);//挂接共享内存
cout<<"shm attch"<<endl;
memset(adress,0,8);
int fd = sny.writeordie();//以写方式打开管道文件
for(char c = 'A'; c < 'Z'; c++)//向文件中写入
{
adress[c-'A'] = c;
sny.Wakeup(fd);//唤醒共享内存
sleep(1);
}
shmdeatch(adress);//取消挂接
cout<<"shm deatch"<<endl;
return 0;
}
//server.cc
#include"comm.hpp"
#define size 8 //共享内存大小为8个字节
using namespace std;
int main()
{
key_t key = getkey();//获取密钥
cout<<"create key"<<endl;
cout<<"key:"<<key<<endl;
int shmid = createShm(key,size);//创建共享内存
cout<<"create shm"<<endl;
cout<<"shmid:"<<shmid<<endl;
char * adress = (char*)shmatch(shmid);//挂接共享内存
cout<<"shm attch"<<endl;
int fd = sny.readordie();//以读方式打开管道
while(true)//向共享内存中写入
{
if(!sny.Wait(fd))//共享内存等待失败就退出
{
cout<<"read over"<<endl;
break;
}
cout<<"content:"<<adress<<endl;
}
shmdeatch(adress);//取消挂接
cout<<"shm deatch"<<endl;
shmdel(shmid);
cout<<"shm del"<<endl;//当共享内存挂接数为0是就删除
return 0;
}
//comm.hpp
#pragma once
#include<iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include<unistd.h>
#include"pipe.hpp"
#define paths "/home/wys"
#define proj 0x66
enum
{
ftok_fail = -1,
createShm_fail = -2,
getshm_fail = -3,
shmat_fail = -4,
shmdel_fail = -5,
};
fifo sny;//创建一个管道,让共享内存有自动协同机制
key_t getkey()
{
key_t ret = ftok(paths,proj);
if(ret == -1)
{
std::cerr<<"ftok fail:"<<strerror(errno)<<std::endl;
return ftok_fail;
}
return ret;
}
int createShm(key_t key,size_t size)
{
int shm_id = shmget(key,size,IPC_CREAT|IPC_EXCL|0666);
if(shm_id == -1)
{
std::cerr<<"shmget fail:"<<strerror(errno)<<std::endl;
return createShm_fail;
}
return shm_id;
}
int getShm(key_t key)
{
int shm_id = shmget(key,0,IPC_CREAT);
if(shm_id == -1)
{
std::cerr<<"shmget fail:"<<strerror(errno)<<std::endl;
return getshm_fail;
}
return shm_id;
}
void * shmatch(int shmid)
{
void *adress = shmat(shmid,nullptr,0);
if((long long int) adress == -1)
{
std::cerr<<"shmat fail:"<<strerror(errno)<<std::endl;
return nullptr;
}
return adress;
}
void shmdeatch(void * adress)
{
int ret = shmdt(adress);
if(ret == -1)
{
std::cerr<<"shmdeatch fail:"<<strerror(errno)<<std::endl;
}
}
void shmdel(int shmid)
{
int ret = shmctl(shmid,IPC_RMID,nullptr);
if(ret == -1)
{
std::cerr<<"shmdel fail:"<<strerror(errno)<<std::endl;
}
else
{
std::cout<<"delete success"<<std::endl;
}
}
#pragma once
#include <iostream>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
#define path "./fifo"
#define mode 0666
class fifo
{
public:
fifo()
{
umask(0);
int ret = mkfifo(_path.c_str(), mode);
if (ret == 0)
{
cout << "mkfifo sucess" << endl;
}
else if (ret == -1)
{
cout << "mkfifo fail,errno:" << errno << "errstirng:" << strerror(errno) << endl;
}
}
int writeordie()
{
int fd = open(path, O_WRONLY);
if (fd == -1)
{
std::cout << "openwrite fail:" << strerror(errno) << endl;
}
return fd;
}
int readordie()
{
int fd = open(path, O_RDONLY);
if (fd == -1)
{
std::cout << "openread fail:" << strerror(errno) << endl;
}
return fd;
}
bool Wait(int fd)
{
bool ret = true;
char c = 0;
int n = read(fd, &c, sizeof(c));
if (n == sizeof(c))
{
std::cout << "wait scuess" << std::endl;
}
else
{
ret = false;
}
return ret;
}
void Wakeup(int fd)
{
char c = 0;
int n = write(fd, &c, sizeof(c));
if (n != sizeof(c))
{
std::cout << "wake up fail" << endl;
}
std::cout << "wake up success" << endl;
}
~fifo()
{
int ret = unlink(_path.c_str());
if (ret == 0)
{
cout << "unlink sucess" << endl;
}
else if (ret == -1)
{
cout << "unlink fail,errno:" << errno << "errstirng:" << strerror(errno) << endl;
}
}
private:
string _path = path;
};