命名管道
管道应用的一个限制就是只能在具有共同祖先的进程间通信,如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它被称为命名管道,命名管道是一种特殊类型的文件
创建命名管道
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}
mkfifo函数可以创建明明管道,第一个参数是指定路径的文件名,第二个参数是创建管道文件的权限
匿名管道与命名管道的区别
匿名函数由pipe函数创建并打开
命名函数由mkfifo函数创建,打开用open
通道一旦建立,那么他们的功能是相似的
用命名管道实现server&client通信
#pragma
#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096
class NamePiped
{
public:
NamePiped(const std::string& path,int who)
:_fifo_path(path),_id(who),_fd(DefaultFd)
{
if(_id==Creater)
{
//服务端创建命名管道
int res=mkfifo(_fifo_path.c_str(),0666);
if(res!=0)
{
perror("mkfifo");
}
std::cout<<" Creater create named pipe "<<std::endl;
}
}
bool OpenForRead()
{
return OpenNamedPipe(Read);
}
bool OpenForWrite()
{
return OpenNamedPipe(Write);
}
int ReadNamePipe(std::string* out)
{
char buffer[BaseSize];
int n=read(_fd,buffer,sizeof(buffer));
if(n>0)
{
buffer[n]=0;
*out=buffer;
}
return n;
}
int WriteNamePipe(const std::string& in)
{
return write(_fd,in.c_str(),in.size());
}
~NamePiped()
{
if(_id==User)
{
int res=unlink(_fifo_path.c_str());
if(res!=0)
{
perror("unlink");
}
std::cout<<" creater free named pipe"<<std::endl;
}
if(_fd!=DefaultFd)
close(_fd);
}
private:
bool OpenNamedPipe(int mode)
{
_fd = open(_fifo_path.c_str(), mode);
if (_fd < 0)
{
false;
}
return true;
}
const std::string _fifo_path;
int _fd;
int _id;
};
#include"NamedPipe.hpp"
//read
int main()
{
NamePiped fifo(comm_path,Creater);
//对于读端而言,如果我们打开文件,文件还没来,会阻塞在open调用中,知道对方写文件,这也叫进程同步
//以读方式打开管道文件
if(fifo.OpenForRead())
{
std::cout<<"server open name pipe done"<<std::endl;
sleep(3);
while(true)
{
std::string message;
int n=fifo.ReadNamePipe(&message);
if(n>0)
{
std::cout<<"Client Saty> " <<message<<std::endl;
}
else if(n==0)
{
std::cout<<"Client quit,Server Too! " <<std::endl;
break;
}
else
{
std::cout<<"fifo.ReadNamePipe Error "<<std::endl;
break;
}
}
}
return 0;
}
#include"NamedPipe.hpp"
//write
int main()
{
NamePiped fifo(comm_path,User);
//以写方式打开管道文件
if(fifo.OpenForWrite())
{
std::cout<<"client open name pipe done"<<std::endl;
while(true)
{
//写数据
std::cout<<"Please Enter>"<<std::endl;
std::string message;
std::getline(std::cin,message);
fifo.WriteNamePipe(message);
}
}
return 0;
}
system V 共享内存
system V 是操作系统中的一种软件接口和系统调用集合的标准
system V 共享内存是一种进程间通信(IPC)机制,他允许多个进程共享一个给定的内存区域,这种通信方式非常高效,因为数据直接在进程间传递,无需数据复制,从而提高了数据传输的效率
我们可以用指令来查看创建的共享内存
ipcs -m
ipcrm -m shmid 删除共享内存
创建共享内存
// 如果没有定义__SHM_HPP__这个宏
#ifndef __SHM_HPP__
// 定义__SHM_HPP__
#define __SHM_HPP__
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/ubuntu/lesson16/shm";
const int gproj_id = 0x66; // 随便写的一个数
const int gShmSize = 4096;
class Shm
{
private:
key_t GetCommKey()
{
// pathname:文件路径名,指向系统中的一个现有文件或目录
// proj_id:项目标识符,通常为一个字符或整数
key_t k = ftok(_pathname.c_str(), _proj_id);
if (k < 0)
{
perror("ftok");
}
return k;
}
int GetShmHelper(key_t key, int size, int flag)
{
// key:一个 key_t 类型的值,用于标识共享内存段
// size:共享内存段的大小,以字节为单位,如果指定的 key 已经存在一个共享内存段,那么这个参数将被忽略。
// shmflg:一组标志位,用于指定共享内存段的权限和其他选项
// 返回值表示共享内存的标识符
int shmid = shmget(key, size, flag);
if (shmid < 0)
{
perror("shmget");
}
return shmid;
}
std::string RoleToString(int who)
{
if (who == gCreater)
return "gCreater";
else if (who == gUser)
return "gUser";
else
return "None";
}
void* AttachShm()
{
if(_addrshm!=nullptr)
{
DetachShm(_addrshm);
}
//shmid:共享内存段的标识符
//shmaddr:指定共享内存段在调用进程地址空间中的位置。如果设置为 NULL,则由系统选择一个合适的地址进行映射
//0:映射为可读写(默认)
//shmat 返回指向共享内存段的指针
void* shmaddr=shmat(_shmid,nullptr,0);
if(shmaddr==nullptr)
{
perror("shmat");
}
std::cout<<"who: "<<RoleToString(_who)<<" attach shm..."<<std::endl;
return shmaddr;
}
void DetachShm(void* shmaddr)
{
//shmdt 函数用于从调用进程的地址空间中分离(或“脱离”)一个先前通过 shmat 函数映射的共享内存段。
//一旦分离,进程将不能再访问该共享内存段
shmdt(shmaddr);
std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
}
public:
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
{
_key = GetCommKey();
if (_who == gCreater)
GetShmUseCreate();
else if (_who == gUser)
GetShmForUse();
_addrshm = AttachShm();
std::cout << "shmid: " << _shmid << std::endl;
std::cout << "key: " << ToHex(_key) << std::endl;
}
~Shm()
{
if (_who == gCreater)
{
// shmid:共享内存标识符,通常通过 shmget 函数获得
// IPC_RMID:删除共享内存段
// buf:一个指向 struct shmid_ds 类型结构的指针,该结构包含共享内存段的状态信息,不需要时可以设置为空
int res = shmctl(_shmid, IPC_RMID, nullptr);
}
std::cout << "shm remove done..." << std::endl;
}
std::string ToHex(key_t key)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%x", key);
return buffer;
}
bool GetShmUseCreate()
{
// PC_CREAT:如果共享内存段不存在,则创建它。
// IPC_EXCL:与 IPC_CREAT 一起使用,如果共享内存段已经存在,则 shmget 调用失败。
// 权限位(如 0600):设置共享内存段的访问权限
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm creat done..." << std::endl;
return false;
}
bool GetShmForUse()
{
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm get done..." << std::endl;
return false;
}
void Zero()
{
if(_addrshm)
{
memset(_addrshm,0,gShmSize);
}
}
void* Addr()
{
return _addrshm;
}
void DebugShm()
{
struct shmid_ds ds;
int n=shmctl(_shmid,IPC_STAT,&ds);
if(n<0)return;
std::cout<<"ds.shm_perm.__key: "<<ToHex(ds.shm_perm.__key)<<std::endl;
std::cout<<"ds.shm_nattch: "<<ds.shm_nattch<<std::endl;
}
private:
key_t _key;
int _shmid;
std::string _pathname;
int _proj_id;
int _who;
void *_addrshm;
};
// 条件编译结束
#endif
#include"Shm.hpp"
#include"NamedPipe.hpp"
int main()
{
// 1. 创建共享内存
Shm shm(gpathname, gproj_id, gCreater);
char *shmaddr = (char*)shm.Addr();
// 2. 创建管道
NamePiped fifo(comm_path, Creater);
fifo.OpenForRead();
while(true)
{
std::string temp;
fifo.ReadNamePipe(&temp);
std::cout << "shm memory content: " << shmaddr << std::endl;
}
//sleep(5);
return 0;
}
#include"Shm.hpp"
#include"NamedPipe.hpp"
int main()
{
//1.创建共享内存
Shm shm(gpathname,gproj_id,gUser);
//清空_addrshm
shm.Zero();
char* shmaddr=(char*)shm.Addr();
sleep(3);
//2.打开管道
NamePiped fifo(comm_path, User);
fifo.OpenForWrite();
//当成string
char ch='A';
while(ch<='Z')
{
shmaddr[ch-'A']=ch;
std::string temp = "wakeup";
std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
fifo.WriteNamePipe(temp);
sleep(1);
ch++;
}
return 0;
}