马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
目录
原理
代码
回归概念
我们前面学习了匿名管道,解决了父子进程(具有血缘关系的进程)之间的通信,那假如两个进程毫无关系呢???
原理
在前面的匿名管道中,可以通过父子继承的方式看到同一个被打开的文件,那么在命名管道中,怎么保证两个毫不干系的进程,打开了同一个文件呢???每一个文件,都有文件路径,文件路径具有唯一性。
由于上面一个进程只是想完成进程间通信,没必要刷新到磁盘上,所以,就要求上图中磁盘上的hello.txt是一种特殊文件,这个文件被打开之后,在内存中一旦写入缓冲区,不会刷新到磁盘上,而是让两者在内存中完成内存级别的通信就可以了,这种通过路径表现通信文件唯一性的,叫做命名管道。所谓命名,就是文件著名字,文件会有路径,有路径肯定著名字。 为什么又叫管道呢?因为它仍旧是一个内存级的基于文件进行通信的通信方案!
代码
我们来介绍一条下令:mkfifo,(fifo即先辈先出,是一个队列),我们在Linux中通过man mkfifo查察:
我们可以使用mkfifo创建命名管道:
myfifo的文件范例是p,我们尝试使用echo往myfifo写一些内容,发现卡在了那里,在另一个终端cat查察,可以看到刚才echo的内容:
echo启动后是一个进程,cat启动后也是一个进程,这就相当于实现了一次通信,
我们发现,在往命名管道写入内容后,myfifo的大小仍然是0,也就是并没有写入磁盘当中,但是两个进程间依然可以通信。
其实,我们也可以使用mkfifo接口创建命名管道,然后一个进程写,一个进程读,使用man 3 mkfifo查察其使用方法:
在创建出命名管道后,我们还需要可以或许删除管道文件,unlink指令可以删除命名管道,
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[BaseSize];
- int n = read(_fd, buffer, sizeof(buffer));
- if(n > 0)
- {
- buffer[n] = 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[BaseSize];
- int n = read(_fd, buffer, sizeof(buffer));
- if(n > 0)
- {
- buffer[n] = 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企服之家,中国第一个企服评测及商务社交产业平台。 |