前文:
Linux–進(jìn)程間的通信-匿名管道
Linux–進(jìn)程間的通信–進(jìn)程池
Linux–進(jìn)程間的通信-命名管道
共享內(nèi)存
對于兩個(gè)進(jìn)程,通過在內(nèi)存開辟一塊空間(操作系統(tǒng)開辟的),進(jìn)程的虛擬地址通過頁表映射到對應(yīng)的共享內(nèi)存空間中,進(jìn)而實(shí)現(xiàn)通信;
特點(diǎn)和作用:
- 高效性: 共享內(nèi)存是一種高效的進(jìn)程間通信方式,因?yàn)樗试S多個(gè)進(jìn)程直接訪問同一塊內(nèi)存,而無需進(jìn)行復(fù)制或數(shù)據(jù)傳輸。
- 快速通信: 由于共享內(nèi)存直接映射到進(jìn)程的地址空間,因此讀寫速度快,適用于對通信速度有較高要求的場景。
- 靈活性: 共享內(nèi)存提供了一種靈活的通信方式,允許多個(gè)進(jìn)程在需要時(shí)訪問共享數(shù)據(jù),而無需通過中間介質(zhì)進(jìn)行通信。
- 數(shù)據(jù)共享: 多個(gè)進(jìn)程可以通過共享內(nèi)存實(shí)現(xiàn)數(shù)據(jù)共享,從而實(shí)現(xiàn)對數(shù)據(jù)的共同讀寫和處理。
模擬實(shí)現(xiàn)
代碼
Comm.hpp:包含共享內(nèi)存的創(chuàng)建,銷毀,掛接進(jìn)程等。
#pragma once
#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;
const char* pathname="/home/ubuntu/Learning/Pipe";
const int proj_id=0x66;
//在內(nèi)核中,共享內(nèi)存的基本單位是4kb,我們申請的大小相當(dāng)于是n*4kb
const int DefaultSize=4096;
//將key值轉(zhuǎn)換為16進(jìn)制的;
string ToHEX(key_t k)
{
char buffer[1024];
snprintf(buffer,sizeof(buffer),"0x%x",k);
return buffer;
}
//獲取鍵值
key_t GetShmKeyorDie()
{
key_t k=ftok(pathname,proj_id);
if(k<0)
{
//當(dāng)返回值為-1時(shí),錯(cuò)誤表示stat(2)系統(tǒng)調(diào)用錯(cuò)誤
cerr << "ftok error, errno : " << errno << ", error string: " << strerror(errno) << endl;
exit(1);
}
return k;
}
//創(chuàng)建共享內(nèi)存,只在該函數(shù)內(nèi)調(diào)用
int CreateShmOrDie(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;
}
//調(diào)用時(shí)的創(chuàng)建共享內(nèi)存
int CreateShm(key_t key,int size)
{
//如果已經(jīng)存在了,那么會(huì)報(bào)錯(cuò);
return CreateShmOrDie(key,size,IPC_CREAT|IPC_EXCL|0666);
}
//調(diào)用時(shí)的獲取
int GetShm(key_t key,int size)
{
return CreateShmOrDie(key,size,IPC_CREAT);
}
//刪除共享內(nèi)存
void DeleteShm(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;
}
}
//查看共享內(nèi)存的狀態(tài)
void ShmDebug(int shmid)
{
struct shmid_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);
//第二個(gè)參數(shù)設(shè)置nullptr,表示讓系統(tǒng)選擇合適的地址進(jìn)行連接
if((long long int)addr==-1)
{
cerr<<"shmat error"<<endl;
return nullptr;
}
return addr;
}
void ShmDetach(void* addr)
{
int n=shmdt(addr);
if(n<0)
{
cerr<<"shmdt error"<<endl;
}
}
fifo.hpp:利用管道來實(shí)現(xiàn)對共享內(nèi)存實(shí)現(xiàn)同步機(jī)制。
#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;
#define Mode 0666
#define Path "./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)
{}
void OpenReadOrDie()
{
_rfd=open(Path,O_RDONLY);
if(_rfd<0)
exit(1);
}
void OpenWriteDie()
{
_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;
}
else if(n==0)
{
ret=false;
}
else
{
return false;
}
return ret;
}
void Wakeup()
{
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>
int main()
{
//1.獲取key
key_t key = GetShmKeyorDie();
std::cout << "key: " << ToHEX(key) << std::endl;
// sleep(2);
//2.創(chuàng)建共享內(nèi)存
int shmid = CreateShm(key, DefaultSize);
std::cout << "shmid: " << shmid << std::endl;
sleep(2);
//4.將共享內(nèi)存與進(jìn)程掛接
char* addr=(char*)ShmAttach(shmid);
cout<<"Attach shm success, addr: "<<ToHEX((uint64_t)addr)<<endl;
//0.先引入管道
fifo ff;
Sync syn;
syn.OpenReadOrDie();
//進(jìn)行通信
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.刪除共享內(nèi)存
DeleteShm(shmid);
return 0;
}
ShmClient.cc
#include"Comm.hpp"
#include "fifo.hpp"
#include<unistd.h>
int main()
{
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;
return 0;
}
解釋
獲取鍵值和創(chuàng)建共享內(nèi)存
如果ftok函數(shù)返回失敗時(shí),我們就需要不斷的嘗試,對路徑名和id值進(jìn)行修改,直至成功。一般來說,有幾種可能:
- 1:如果傳入的路徑名不存在
- 2:傳入的路徑名沒有讀取權(quán)限,無法讀取該文件的索引節(jié)點(diǎn)號
- 3:文件的索引節(jié)點(diǎn)超過了8位,即超過了一個(gè)字節(jié)的范圍
- 4:系統(tǒng)中已經(jīng)使用了所有的IPC鍵值
刪除共享內(nèi)存
查看共享內(nèi)存的狀態(tài)
掛接進(jìn)程
進(jìn)入通信
文章來源:http://www.zghlxwxcb.cn/news/detail-858535.html
協(xié)同機(jī)制
文章來源地址http://www.zghlxwxcb.cn/news/detail-858535.html
到了這里,關(guān)于Linux--進(jìn)程間的通信-共享內(nèi)存的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!