【Linux】命名管道
目录原理
代码
回归概念
我们前面学习了匿名管道,解决了父子进程(具有血缘关系的进程)之间的通信,那假如两个进程毫无关系呢???
原理
在前面的匿名管道中,可以通过父子继承的方式看到同一个被打开的文件,那么在命名管道中,怎么保证两个毫不干系的进程,打开了同一个文件呢???每一个文件,都有文件路径,文件路径具有唯一性。
https://i-blog.csdnimg.cn/direct/22679201fbae49a491041130ee76f2d1.png
由于上面一个进程只是想完成进程间通信,没必要刷新到磁盘上,所以,就要求上图中磁盘上的hello.txt是一种特殊文件,这个文件被打开之后,在内存中一旦写入缓冲区,不会刷新到磁盘上,而是让两者在内存中完成内存级别的通信就可以了,这种通过路径表现通信文件唯一性的,叫做命名管道。所谓命名,就是文件著名字,文件会有路径,有路径肯定著名字。 为什么又叫管道呢?因为它仍旧是一个内存级的基于文件进行通信的通信方案!
代码
我们来介绍一条下令:mkfifo,(fifo即先辈先出,是一个队列),我们在Linux中通过man mkfifo查察:
https://i-blog.csdnimg.cn/direct/04a1b41881d342f2847ce49a9664a2f8.png
我们可以使用mkfifo创建命名管道:
https://i-blog.csdnimg.cn/direct/3fa42f4178b64387b9bc7df8e921cdbb.png
myfifo的文件范例是p,我们尝试使用echo往myfifo写一些内容,发现卡在了那里,在另一个终端cat查察,可以看到刚才echo的内容:
https://i-blog.csdnimg.cn/direct/d6dee1bc69dc4d6d82f7725718e78baa.png
echo启动后是一个进程,cat启动后也是一个进程,这就相当于实现了一次通信,
https://i-blog.csdnimg.cn/direct/a95b78561d144beab9d3d1b41885b72c.png
我们发现,在往命名管道写入内容后,myfifo的大小仍然是0,也就是并没有写入磁盘当中,但是两个进程间依然可以通信。
其实,我们也可以使用mkfifo接口创建命名管道,然后一个进程写,一个进程读,使用man 3 mkfifo查察其使用方法:
https://i-blog.csdnimg.cn/direct/92c14d4024a74c55ab2c2b89d7caa360.png
https://i-blog.csdnimg.cn/direct/068b423b8101468db1a19f556c948d2b.png
在创建出命名管道后,我们还需要可以或许删除管道文件,unlink指令可以删除命名管道,
https://i-blog.csdnimg.cn/direct/3e449eda92624a52b04cdcbb85c9d0f4.png
https://i-blog.csdnimg.cn/direct/fd78d72dcab04d898adabd540b092fbb.png
unlink的参数是文件路径,通过路径删除指定路径下的文件,因此,我们在namedPipe.hpp文件中可以写两个函数,一个用于创建管道,一个用于删除管道:
#pragma once
#include<iostream>
#include<string>
#include<cstdio>
#include<cerrno>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
const std::string comm_path = "./myfifo";
int CreateNamedPipe(const std::string& path)
{
int res = mkfifo(path.c_str(), 0666);
if(res < 0)
{
perror("mkfifo");
}
return res;
}
int RemoveNamedPipe(const std::string& path)
{
int res = unlink(path.c_str());
if(res < 0)
{
perror("mkfifo");
}
return res;
}
一般来说,我们建议由一个进程来创建和删除命名管道,即由一个进程来管理管道,将来另一个进程想和这个进程通信就不用再创建管道,而只需打开管道文件就可以,也就是一个以读方式打开,一个以写方式打开,就可以完成通信了!
为了让进程更好地管理命名管道,我们为命名管道创建一个类NamedPipe():
class NamedPipe
{
private:
bool OpenNamedPipe(int mode)
{
_fd = open(_fifo_path.c_str(), mode);
if (_fd < 0)
return false;
return true;
}
public:
NamedPipe(const std::string &path, int who)
: _fifo_path(path), _id(who), _fd(DefaultFd)
{
if (who == Creater)
{
int res = mkfifo(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 ReadNamedPipe(std::string* out)
{
char buffer;
int n = read(_fd, buffer, sizeof(buffer));
if(n > 0)
{
buffer = 0;
*out = buffer;
}
return n;
}
int WriteNamedPipe(const std::string& in)
{
return write(_fd, in.c_str(), in.size());
}
~NamedPipe()
{
if (_id == Creater)
{
//sleep(5);
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:
const std::string _fifo_path;
int _id;
int _fd;
}; 我们来看一种现象,先只运行读端,假如打开文件,但是写还没来,会壅闭在open调用中,直到对方打开,是一种进程同步。假如读端和写端都打开了,此时关闭读端,假如写端再写入,那么写端就会关闭,因为受到了SIGPIPE信号。综合来看,这种命名管道也遵循我们之前说的管道的所有特性!
回归概念
我们知道,进程间通信的前提是让不同的进程,看到同一份资源,对于命名管道来说,通过文件路径看到了同一份资源!
完备代码附上:
namedPipe.hpp
#pragma once#include <iostream>#include <string>#include <cstdio>#include <cerrno>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define Creater 1#define User 2#define BaseSize 4096#define DefaultFd -1#define Read O_RDONLY#define Write O_WRONLYconst std::string comm_path = "./myfifo";class NamedPipe
{
private:
bool OpenNamedPipe(int mode)
{
_fd = open(_fifo_path.c_str(), mode);
if (_fd < 0)
return false;
return true;
}
public:
NamedPipe(const std::string &path, int who)
: _fifo_path(path), _id(who), _fd(DefaultFd)
{
if (who == Creater)
{
int res = mkfifo(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 ReadNamedPipe(std::string* out)
{
char buffer;
int n = read(_fd, buffer, sizeof(buffer));
if(n > 0)
{
buffer = 0;
*out = buffer;
}
return n;
}
int WriteNamedPipe(const std::string& in)
{
return write(_fd, in.c_str(), in.size());
}
~NamedPipe()
{
if (_id == Creater)
{
//sleep(5);
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:
const std::string _fifo_path;
int _id;
int _fd;
}; server.cc
#include "namedPipe.hpp"
// server read:管理命名管道的整个生命周期
int main()
{
NamedPipe fifo(comm_path, Creater);
if (fifo.OpenForRead())
{
//对于读端而言,如果我们打开文件,但是写还没来,我会阻塞在open调用中,直到对方打开
//进程同步
std::cout << "server open named pipe done" << std::endl;
sleep(3);
while (true)
{
std::string message;
int n = fifo.ReadNamedPipe(&message);
if (n > 0)
{
std::cout << "CLient say: " << message << std::endl;
}
else if(n == 0)
{
std::cout<<"Client quit,Server Too!" <<std::endl;
break;
}
else
{
std::cout<<"fifo.ReadNamedPipe Error" <<std::endl;
break;
}
}
}
return 0;
} client.cc
#include "namedPipe.hpp"
// write
int main()
{
NamedPipe fifo(comm_path, User);
if (fifo.OpenForWrite())
{
std::cout << "client open named pipe done" << std::endl;
while (true)
{
std::cout << "please enter> ";
std::string message;
std::getline(std::cin, message);
fifo.WriteNamedPipe(message);
}
}
return 0;
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]