IT评测·应用市场-qidao123.com

标题: 【Linux】命名管道 [打印本页]

作者: 篮之新喜    时间: 2024-9-26 18:44
标题: 【Linux】命名管道
目录
原理
代码
回归概念


我们前面学习了匿名管道,解决了父子进程(具有血缘关系的进程)之间的通信,那假如两个进程毫无关系呢???
原理

在前面的匿名管道中,可以通过父子继承的方式看到同一个被打开的文件,那么在命名管道中,怎么保证两个毫不干系的进程,打开了同一个文件呢???每一个文件,都有文件路径,文件路径具有唯一性。

由于上面一个进程只是想完成进程间通信,没必要刷新到磁盘上,所以,就要求上图中磁盘上的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文件中可以写两个函数,一个用于创建管道,一个用于删除管道:
  1. #pragma once
  2. #include<iostream>
  3. #include<string>
  4. #include<cstdio>
  5. #include<cerrno>
  6. #include<unistd.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. const std::string comm_path = "./myfifo";
  10. int CreateNamedPipe(const std::string& path)
  11. {
  12.     int res = mkfifo(path.c_str(), 0666);
  13.     if(res < 0)
  14.     {
  15.         perror("mkfifo");
  16.     }
  17.     return res;
  18. }
  19. int RemoveNamedPipe(const std::string& path)
  20. {
  21.     int res = unlink(path.c_str());
  22.     if(res < 0)
  23.     {
  24.         perror("mkfifo");
  25.     }
  26.     return res;
  27. }
复制代码
一般来说,我们建议由一个进程来创建和删除命名管道,即由一个进程来管理管道,将来另一个进程想和这个进程通信就不用再创建管道,而只需打开管道文件就可以,也就是一个以读方式打开,一个以写方式打开,就可以完成通信了!
为了让进程更好地管理命名管道,我们为命名管道创建一个类NamedPipe():
  1. class NamedPipe
  2. {
  3. private:
  4.     bool OpenNamedPipe(int mode)
  5.     {
  6.         _fd = open(_fifo_path.c_str(), mode);
  7.         if (_fd < 0)
  8.             return false;
  9.         return true;
  10.     }
  11. public:
  12.     NamedPipe(const std::string &path, int who)
  13.         : _fifo_path(path), _id(who), _fd(DefaultFd)
  14.     {
  15.         if (who == Creater)
  16.         {
  17.             int res = mkfifo(path.c_str(), 0666);
  18.             if (res < 0)
  19.             {
  20.                 perror("mkfifo");
  21.             }
  22.             std::cout << "creater create named pipe" << std::endl;
  23.         }
  24.     }
  25.     bool OpenForRead()
  26.     {
  27.         return OpenNamedPipe(Read);
  28.     }
  29.     bool OpenForWrite()
  30.     {
  31.         return OpenNamedPipe(Write);
  32.     }
  33.     int ReadNamedPipe(std::string* out)
  34.     {
  35.         char buffer[BaseSize];
  36.         int n = read(_fd, buffer, sizeof(buffer));
  37.         if(n > 0)
  38.         {
  39.             buffer[n] = 0;
  40.             *out = buffer;
  41.         }
  42.         return n;
  43.     }
  44.     int WriteNamedPipe(const std::string& in)
  45.     {
  46.         return write(_fd, in.c_str(), in.size());
  47.     }
  48.     ~NamedPipe()
  49.     {
  50.         if (_id == Creater)
  51.         {
  52.             //sleep(5);
  53.             int res = unlink(_fifo_path.c_str());
  54.             if (res < 0)
  55.             {
  56.                 perror("unlink");
  57.             }
  58.             std::cout << "creater free named pipe" << std::endl;
  59.         }
  60.         if(_fd != DefaultFd) close(_fd);
  61.     }
  62. private:
  63.     const std::string _fifo_path;
  64.     int _id;
  65.     int _fd;
  66. };
复制代码
我们来看一种现象,先只运行读端,假如打开文件,但是写还没来,会壅闭在open调用中,直到对方打开,是一种进程同步。假如读端和写端都打开了,此时关闭读端,假如写端再写入,那么写端就会关闭,因为受到了SIGPIPE信号。综合来看,这种命名管道也遵循我们之前说的管道的所有特性!
回归概念

我们知道,进程间通信的前提是让不同的进程,看到同一份资源,对于命名管道来说,通过文件路径看到了同一份资源!
完备代码附上:
namedPipe.hpp
  1. #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
  2. {
  3. private:
  4.     bool OpenNamedPipe(int mode)
  5.     {
  6.         _fd = open(_fifo_path.c_str(), mode);
  7.         if (_fd < 0)
  8.             return false;
  9.         return true;
  10.     }
  11. public:
  12.     NamedPipe(const std::string &path, int who)
  13.         : _fifo_path(path), _id(who), _fd(DefaultFd)
  14.     {
  15.         if (who == Creater)
  16.         {
  17.             int res = mkfifo(path.c_str(), 0666);
  18.             if (res < 0)
  19.             {
  20.                 perror("mkfifo");
  21.             }
  22.             std::cout << "creater create named pipe" << std::endl;
  23.         }
  24.     }
  25.     bool OpenForRead()
  26.     {
  27.         return OpenNamedPipe(Read);
  28.     }
  29.     bool OpenForWrite()
  30.     {
  31.         return OpenNamedPipe(Write);
  32.     }
  33.     int ReadNamedPipe(std::string* out)
  34.     {
  35.         char buffer[BaseSize];
  36.         int n = read(_fd, buffer, sizeof(buffer));
  37.         if(n > 0)
  38.         {
  39.             buffer[n] = 0;
  40.             *out = buffer;
  41.         }
  42.         return n;
  43.     }
  44.     int WriteNamedPipe(const std::string& in)
  45.     {
  46.         return write(_fd, in.c_str(), in.size());
  47.     }
  48.     ~NamedPipe()
  49.     {
  50.         if (_id == Creater)
  51.         {
  52.             //sleep(5);
  53.             int res = unlink(_fifo_path.c_str());
  54.             if (res < 0)
  55.             {
  56.                 perror("unlink");
  57.             }
  58.             std::cout << "creater free named pipe" << std::endl;
  59.         }
  60.         if(_fd != DefaultFd) close(_fd);
  61.     }
  62. private:
  63.     const std::string _fifo_path;
  64.     int _id;
  65.     int _fd;
  66. };
复制代码
server.cc
  1. #include "namedPipe.hpp"
  2. // server read:管理命名管道的整个生命周期
  3. int main()
  4. {
  5.     NamedPipe fifo(comm_path, Creater);
  6.     if (fifo.OpenForRead())
  7.     {
  8.         //对于读端而言,如果我们打开文件,但是写还没来,我会阻塞在open调用中,直到对方打开
  9.         //进程同步
  10.         std::cout << "server open named pipe done" << std::endl;
  11.         sleep(3);
  12.         while (true)
  13.         {
  14.             std::string message;
  15.             int n = fifo.ReadNamedPipe(&message);
  16.             if (n > 0)
  17.             {
  18.                 std::cout << "CLient say: " << message << std::endl;
  19.             }
  20.             else if(n == 0)
  21.             {
  22.                 std::cout<<"Client quit,Server Too!" <<std::endl;
  23.                 break;
  24.             }
  25.             else
  26.             {
  27.                 std::cout<<"fifo.ReadNamedPipe Error" <<std::endl;
  28.                 break;
  29.             }
  30.         }
  31.     }
  32.     return 0;
  33. }
复制代码
client.cc
  1. #include "namedPipe.hpp"
  2. // write
  3. int main()
  4. {
  5.     NamedPipe fifo(comm_path, User);
  6.     if (fifo.OpenForWrite())
  7.     {
  8.         std::cout << "client open named pipe done" << std::endl;
  9.         while (true)
  10.         {
  11.             std::cout << "please enter> ";
  12.             std::string message;
  13.             std::getline(std::cin, message);
  14.             fifo.WriteNamedPipe(message);
  15.         }
  16.     }
  17.     return 0;
  18. }
复制代码






 


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4