前文:
Linux–进程间的通信-匿名管道
Linux–进程间的通信–进程池
Linux–进程间的通信-命名管道
共享内存
对于两个进程,通过在内存开辟一块空间(操作系统开辟的),进程的虚拟地址通过页表映射到对应的共享内存空间中,进而实现通信;
特点和作用:
- 高效性: 共享内存是一种高效的进程间通信方式,因为它允许多个进程直接访问同一块内存,而无需进行复制或数据传输。
- 快速通信: 由于共享内存直接映射到进程的地址空间,因此读写速度快,适用于对通信速度有较高要求的场景。
- 灵活性: 共享内存提供了一种灵活的通信方式,允许多个进程在需要时访问共享数据,而无需通过中间介质进行通信。
- 数据共享: 多个进程可以通过共享内存实现数据共享,从而实现对数据的共同读写和处理。
模拟实现
代码
Comm.hpp:包含共享内存的创建,销毁,挂接进程等。
#pragmaonce#include<stdio.h>#include<iostream>#include<string>#include<cerrno>#include<cstring>#include<cstdlib>#include<sys/ipc.h>#include<sys/types.h>#include<sys/shm.h>
using namespace std;constchar* pathname="/home/ubuntu/Learning/Pipe";constint proj_id=0x66;//在内核中,共享内存的基本单位是4kb,我们申请的大小相当于是n*4kbconstint DefaultSize=4096;//将key值转换为16进制的;
string ToHEX(key_t k){char buffer[1024];snprintf(buffer,sizeof(buffer),"0x%x",k);return buffer;}//获取键值key_tGetShmKeyorDie(){key_t k=ftok(pathname,proj_id);if(k<0){//当返回值为-1时,错误表示stat(2)系统调用错误
cerr <<"ftok error, errno : "<< errno <<", error string: "<<strerror(errno)<< endl;exit(1);}return k;}//创建共享内存,只在该函数内调用intCreateShmOrDie(key_t key,int size,int flag){int shmid =shmget(key,size,flag);if(shmid<0){
std::cerr <<"shmget error, errno : "<< errno <<", error string: "<<strerror(errno)<< std::endl;exit(2);}return shmid;}//调用时的创建共享内存intCreateShm(key_t key,int size){//如果已经存在了,那么会报错;returnCreateShmOrDie(key,size,IPC_CREAT|IPC_EXCL|0666);}//调用时的获取intGetShm(key_t key,int size){returnCreateShmOrDie(key,size,IPC_CREAT);}//删除共享内存voidDeleteShm(int shmid){int n=shmctl(shmid,IPC_RMID,nullptr);if(n<0){
cerr<<"shmctl error"<<endl;}else{
cout<<"shmctl delete shm success, shmid: "<<shmid<<endl;}}//查看共享内存的状态voidShmDebug(int shmid){structshmid_ds shmds;int n=shmctl(shmid ,IPC_STAT,&shmds);if(n<0){
std::cerr <<"shmctl error"<< std::endl;return;}
std::cout <<"shmds.shm_segsz: "<< shmds.shm_segsz << std::endl;
std::cout <<"shmds.shm_nattch:"<< shmds.shm_nattch << std::endl;
std::cout <<"shmds.shm_ctime:"<< shmds.shm_ctime << std::endl;
std::cout <<"shmds.shm_perm.__key:"<<ToHEX(shmds.shm_perm.__key)<< std::endl;}void*ShmAttach(int shmid){void* addr =shmat(shmid,nullptr,0);//第二个参数设置nullptr,表示让系统选择合适的地址进行连接if((longlongint)addr==-1){
cerr<<"shmat error"<<endl;return nullptr;}return addr;}voidShmDetach(void* addr){int n=shmdt(addr);if(n<0){
cerr<<"shmdt error"<<endl;}}
fifo.hpp:利用管道来实现对共享内存实现同步机制。
#include<iostream>#include<string>#include<cstring>#include<cerrno>#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>#include<fcntl.h>#include<assert.h>
using namespace std;#defineMode0666#definePath"./fifo"
class fifo
{
public:fifo(const string & path=Path):_path(path){umask(0);int n=mkfifo(_path.c_str(),Mode);if(n==0){
cout<<"mkfifo success"<< endl;}else{
cerr <<"mkfifo failed, errno: "<< errno <<", errstring: "<<strerror(errno)<< endl;}}~fifo(){int n=unlink(_path.c_str());if(n ==0){
cout <<"remove fifo file "<< _path <<" success"<< endl;}else{
cerr <<"remove failed, errno: "<< errno <<", errstring: "<<strerror(errno)<< endl;}}
private:
string _path;//文件路径};
class Sync
{
public:Sync():_rfd(-1),_wfd(-1){}voidOpenReadOrDie(){
_rfd=open(Path,O_RDONLY);if(_rfd<0)exit(1);}voidOpenWriteDie(){
_wfd=open(Path,O_WRONLY);if(_wfd<0)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<<"server wakeup ,begin read shm..."<<endl;}elseif(n==0){
ret=false;}else{return false;}return ret;}voidWakeup(){uint32_t c=0;ssize_t n=write(_wfd,&c,sizeof(c));assert(n==sizeof(uint32_t));
cout<<"wakeup server..."<<endl;}~Sync(){}
private:int _wfd;int _rfd;};
ShmServer.cc
#include"Comm.hpp"#include"fifo.hpp"#include<unistd.h>intmain(){//1.获取keykey_t key =GetShmKeyorDie();
std::cout <<"key: "<<ToHEX(key)<< std::endl;// sleep(2);//2.创建共享内存int shmid =CreateShm(key, DefaultSize);
std::cout <<"shmid: "<< shmid << std::endl;sleep(2);//4.将共享内存与进程挂接char* addr=(char*)ShmAttach(shmid);
cout<<"Attach shm success, addr: "<<ToHEX((uint64_t)addr)<<endl;//0.先引入管道
fifo ff;
Sync syn;
syn.OpenReadOrDie();//进行通信while(1){if(!syn.Wait())break;
cout<<"shm content: "<<addr<<endl;}ShmDetach(addr);
std::cout <<"Detach shm success, addr: "<<ToHEX((uint64_t)addr)<< std::endl;//3.删除共享内存DeleteShm(shmid);return0;}
ShmClient.cc
#include"Comm.hpp"#include"fifo.hpp"#include<unistd.h>intmain(){key_t key =GetShmKeyorDie();
std::cout <<"key: "<<ToHEX(key)<< std::endl;// sleep(2);int shmid =GetShm(key, DefaultSize);
std::cout <<"shmid: "<< shmid << std::endl;char* addr=(char*)ShmAttach(shmid);
cout<<"Attach shm success, addr: "<<ToHEX((uint64_t)addr)<<endl;//通信memset(addr,0,DefaultSize);
Sync syn;
syn.OpenWriteDie();for(char c ='A';c<='Z';c++){
addr[c-'A']=c;sleep(1);
syn.Wakeup();}ShmDetach(addr);
std::cout <<"Detach shm success, addr: "<<ToHEX((uint64_t)addr)<< std::endl;return0;}
解释
获取键值和创建共享内存
如果ftok函数返回失败时,我们就需要不断的尝试,对路径名和id值进行修改,直至成功。一般来说,有几种可能:
- 1:如果传入的路径名不存在
- 2:传入的路径名没有读取权限,无法读取该文件的索引节点号
- 3:文件的索引节点超过了8位,即超过了一个字节的范围
- 4:系统中已经使用了所有的IPC键值
删除共享内存
查看共享内存的状态
挂接进程
进入通信
协同机制
版权归原作者 诡异森林。 所有, 如有侵权,请联系我们删除。