目录
一、共享内存
共享内存的原理:
进程A把它的数据在地址空间中通过页表映射到物理内存中,进程B通过页表获取物理内存的物理地址,得到数据。
共享内存在物理内存可能有多个,那么两个进程如何确定找到的是同一个共享内存呢?所以要给每个共享内存提供唯一的标识。
key_t key是标识共享内存唯一性的。shmflg有3种,IPC_CREAT:如果共享内存不存在,就创建,如果已经存在,就获取它;IPC_EXCL:不能单独使用,没有意义;IPC_CREAT || IPC_EXCL:如果共享内存不存在,就创建,否则出错返回。第3个与第1个的区别是只要共享内存创建成功,那么一定是新的共享内存。
key通过系统调用ftok获取
ftok的参数由用户提供,通信的两个进程都是同样的pathname和proj_id,这样返回的key是相同的,使用的就是同一个共享内存了。
使用共享内存,一定是一个进程创建共享内存,另一个进程获取。共享内存如果我们没有主动释放,它就会一直存在,因为共享内存是随系统的,除非重启系统,否则一直存在。而文件是随进程的。
通过指令查看共享内存:ipcs -m;通过指令删除共享内存:ipcrm -m shmid
挂接的意义:可使用add返回值,直接访问共享内存。nattch表示几个进程与共享内存相关联
共享内存不提供进程间的协同机制:当只有读端时,写端不写,读端不会发生阻塞等待。
共享内存的优缺点:优点:共享内存是所有进程间通信中最快的;缺点:不提供进程间的协同机制,因此会有数据传输不一致的问题。为什么最快?因为一个进程将数据传输到物理内存,另一个进程就能通过页表将数据返回虚拟地址,这个过程数据只拷贝了一次,而管道至少要两次。
代码:
Comm.hpp
#ifndef __COMM_HPP__
#define __COMM_HPP__
#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/shm.h>
using namespace std;
const char* path = "/home/yss";
int proj_id = 0x66;
int defaultsize = 4096;
//16进制转换
string ToHex(key_t key)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "0x%x", key);
return buffer;
}
//获取key
key_t getkeyOrdie()
{
key_t k = ftok(path, proj_id);
if(k < 0)
{
cout << "getkey fail, error: " << errno << "stringerror: " << strerror(errno) << endl;
exit(1);
}
return k;
}
//获取shm
int getShmOrdie(key_t key, size_t size, int flag)
{
int shmid = shmget(key, size, flag);
if(shmid < 0)
{
cout << "getshm fail, error: " << errno << "stringerror: " << strerror(errno) << endl;
exit(1);
}
return shmid;
}
//创建shm
int CreateShm(key_t key, size_t size)
{
return getShmOrdie(key, size, IPC_CREAT|IPC_EXCL|0666);
}
//得到shm
int GetShm(key_t key, size_t size)
{
return getShmOrdie(key, size, IPC_CREAT);
}
//删除shm
void DeleteShm(int shmid)
{
int n = shmctl(shmid, IPC_RMID, nullptr);
if(n < 0)
{
cout << "delete fail" << endl;
}
else
{
cout << "delete success" << endl;
}
}
//挂接shm
void* AttachShm(int shmid)
{
void* addr = shmat(shmid, nullptr, 0);
if((long long int)addr == -1)
{
cout << "attach fail" << endl;
return nullptr;
}
return addr;
}
//去挂接
void DetachShm(void* addr)
{
int n = shmdt(addr);
if(n < 0)
{
cout << "detach fail" << endl;
}
}
#endif
Fifo.hpp
#ifndef __FIFO_FPP__
#define __FIFO_FPP__
#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <cstdlib>
#include <fcntl.h>
#include <cassert>
using namespace std;
#define Mode 0666
#define Path "fifo"
class Fifo
{
public:
Fifo(string path = Path) : _path(path)
{
umask(0);
//创建管道
int n = mkfifo(_path.c_str(), Mode);
if(n == 0)
{
cout << "fifo create success" <<endl;
}
else
{
cout << "fifo fail, errno: " << errno << " stringerrno: " << strerror(errno) << endl;
}
}
~Fifo()
{
//删除管道,管道也是文件
int n = unlink(_path.c_str());
if(n == 0)
{
cout << "fifo remove success" <<endl;
}
else
{
cout << "fifo remove, errno: " << errno << " stringerrno: " << strerror(errno) << endl;
}
}
private:
string _path;//文件名+文件路径
};
class Sync
{
public:
Sync() :wfd(-1), rfd(-1)
{}
void OpenWrite()
{
wfd = open(Path, O_WRONLY);
if(wfd < 0)
{
cout << "writeopen fail" <<endl;
exit(1);
}
}
void OpenRead()
{
rfd = open(Path, O_RDONLY);
if(rfd < 0)
{
cout << "readopen fail" <<endl;
exit(1);
}
}
bool Wait()
{
bool ret = true;
uint32_t c = 0;
ssize_t n = read(rfd, &c, sizeof(uint32_t));
if(n == sizeof(uint32_t))
{
cout << "等待后,读取数据..." << endl;
}
else if(n == 0)
{
ret = false;
}
else
{
return false;
}
return ret;
}
void Wakeup()
{
uint32_t c = 0;
ssize_t n = write(wfd, &c, sizeof(uint32_t));
assert(n == sizeof(uint32_t));
cout << "唤醒..." << endl;
}
~Sync()
{}
private:
int wfd;
int rfd;
};
#endif
ShmServer.cc
#include "Comm.hpp"
#include "Fifo.hpp"
int main()
{
//获取key
key_t key = getkeyOrdie();
cout << "key: " << ToHex(key) << endl;
//创建共享内存
int shmid = CreateShm(key, defaultsize);
cout << "shmid: " << shmid << endl;
//挂接
char* addr = (char*)AttachShm(shmid);
cout << "attach success" << endl;
//引入管道
Fifo fifo;
Sync syn;
syn.OpenRead();
//通信
for(;;)
{
if(!syn.Wait()) break;
cout << "addr message: " <<addr<<endl;
sleep(1);
}
//去挂接
DetachShm(addr);
cout << "detach success" << endl;
//删除共享内存
DeleteShm(shmid);
return 0;
}
ShmClient.cc
#include "Comm.hpp"
#include "Fifo.hpp"
int main()
{
key_t key = getkeyOrdie();
cout << "key: " << ToHex(key) << endl;
int shmid = GetShm(key, defaultsize);
cout << "shmid: " << shmid << endl;
//挂接
char* addr = (char*)AttachShm(shmid);
cout << "attach success" << endl;
memset(addr, 0, defaultsize);
Sync syn;
syn.OpenWrite();
//通信
for(int i='A'; i<='Z'; i++)
{
addr[i-'A'] = i;
sleep(1);
syn.Wakeup();
}
//去挂接
DetachShm(addr);
cout << "detach success" << endl;
sleep(5);
return 0;
}