当前位置: 首页 > news >正文

【Linux】第三十二站:命名管道

文章目录

  • 一、命名管道介绍
  • 二、编码
    • 1.mkfifo
    • 2.unlink
    • 3.一个简单的例子
    • 4.修改

一、命名管道介绍

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。

如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

命名管道是一种特殊类型的文件

image-20240120181817217

我们可以直接使用下面的命令去创建命名管道

mkfifo myfifo

image-20240120182450551

它的文件类型是以p开头的,也就是命名管道

其实这个myfifo命名管道文件,它在磁盘中并没有数据,它更多的只是一种符号

如下所示

当我们左侧将内容输入到管道的时候,左侧会先进入阻塞。直到右侧使用cat拿到数据以后,左侧才会结束

image-20240120182624167

首先这两个指令是两个进程,这两个进程是毫无关系,但是他们是可以利用这个命名管道进行通信的

也可以看到,在写入的过程中,命名管道的大小一直是0

image-20240120183043910

我们也知道,如果两个不同的进程,打开同一个文件的时候,在内核中,操作系统也是打开一个文件,还是我们前面的这一套

image-20240120210716329

所以:

进程间通信的前提:先让不同的进程看到同一份资源

现在我们的这个管道文件其实就是一个内存级文件,它是不需要刷盘的!

那么我们怎么保证我们两个进程打开的是同一个文件呢?

只要同路径下的同一个文件名即可–>路径 + 文件名具有唯一性

二、编码

1.mkfifo

如下所示

image-20240120212754825

上面的函数可以去创建一个管道文件,第一个参数是路径,第二个是管道的权限是什么,如果成功是0,否则返回-1

如下代码所示

image-20240121123911378

image-20240121123855603

2.unlink

如果我们想要删除一个文件我们可以使用unlink接口

image-20240121124057956

如果成功,返回,如果失败返回-1

如下代码所示

image-20240121124451755

我们可以先编译运行一下,我们会发现会先报错说管道文件已经存在,这是正常的,因为我们之前的并没有删除掉

image-20240121124438918

当我们将原来的管道删除以后,重新运行,就可以看到了

image-20240121125130086

3.一个简单的例子

#pragma once
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#define FIFO_FILE "./myfifo"
#define MODE 0664enum 
{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR};
using namespace std;
#include "comm.hpp"int main()
{int fd = open(FIFO_FILE,O_WRONLY);if(fd < 0){perror("clinet open file fail...");exit(FIFO_OPEN_ERR);}cout << "client open success" <<endl;string line;while(1){cout << "Please Enter@ ";getline(cin,line);write(fd, line.c_str(), line.size());}close(fd);cout << "客户端关闭啦!..." << endl;return 0;
}
#include "comm.hpp"
using namespace std;int main()
{//创建一个管道int n = mkfifo(FIFO_FILE, MODE);if(n == -1) {perror("mkfifo");exit(FIFO_CREATE_ERR);}//开始通信int fd = open(FIFO_FILE,O_RDONLY);if(fd < 0){perror("open:");exit(FIFO_OPEN_ERR);}cout <<"server open file sueccess" <<endl;while(1){char buffer[1024] = {0}; ssize_t x = read(fd, buffer, sizeof(buffer) - 1);if(x > 0){buffer[x] = 0;cout << "服务器收到客户端发送的消息:"<< buffer <<endl;}else if(x == 0){cout << "客户端关闭,服务器关闭" << endl;break;}else {break;}}close(fd);//关闭信道int m = unlink(FIFO_FILE);if(m == -1){perror("unlink:");exit(FIFO_DELETE_ERR);}return 0;
}

如上代码所示,最终的运行结果为

当我们先打开服务端的时候,我们发现服务端创建了管道,但是并没有打印出server open file success,这说明open处阻塞了

image-20240121153225268

当我们一旦打开了客户端,服务端和客户端几乎同时打开成功。这说明,命名管道文件需要写端和读端都打开的时候才可以,否则只打开其中一个会进入阻塞。我们可以理解为这是为了防止只打开读端,不打开写端的管道会出现读入0的情况。只打开写端,不打开读端会杀掉写端的进程

image-20240121153337884

然后就可以通信了

最终我们我们关闭客户端的同时,由于关闭了写端,但是读端没有关闭,就会读入0,最终服务端的代码逻辑会检测到这个0,从而结束进程

image-20240121153654275

同样的,如果我们先关闭了服务端,那么就会杀掉客户端的进程

image-20240121153931796

4.修改

我们现在对上面的代码进行一下小小的修改,使代码变得更加优雅

如下所示,我们让创建管道和销毁管道变成一个类,这样的话,就不需要我们自己去手动控制了

#pragma once
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string>
#define FIFO_FILE "./myfifo"
#define MODE 0664enum 
{FIFO_CREATE_ERR = 1,FIFO_DELETE_ERR,FIFO_OPEN_ERR};class Init
{
public:Init(){//创建一个管道int n = mkfifo(FIFO_FILE, MODE);if(n == -1) {perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){//关闭信道int m = unlink(FIFO_FILE);if(m == -1){perror("unlink:");exit(FIFO_DELETE_ERR);}}};

客户端还是不变的

using namespace std;
#include "comm.hpp"int main()
{int fd = open(FIFO_FILE,O_WRONLY);if(fd < 0){perror("clinet open file fail...");exit(FIFO_OPEN_ERR);}cout << "client open success" <<endl;string line;while(1){cout << "Please Enter@ ";getline(cin,line);write(fd, line.c_str(), line.size());}close(fd);cout << "客户端关闭啦!..." << endl;return 0;
}

下面是服务端,就可以通过一个变量的定义来控制前面的创建管道和关闭管道了

#include "comm.hpp"
using namespace std;int main()
{Init in;//开始通信int fd = open(FIFO_FILE,O_RDONLY);if(fd < 0){perror("open:");exit(FIFO_OPEN_ERR);}cout <<"server open file sueccess" <<endl;while(1){char buffer[1024] = {0}; ssize_t x = read(fd, buffer, sizeof(buffer) - 1);if(x > 0){buffer[x] = 0;cout << "服务器收到客户端发送的消息:"<< buffer <<endl;}else if(x == 0){cout << "客户端关闭,服务器关闭" << endl;break;}else {break;}}close(fd);return 0;
}

相关文章:

  • Qt5.12.0 与 VS2017 在 .pro文件转.vcxproj文件
  • easyui渲染隐藏域<input type=“hidden“ />为textbox可作为分割条使用
  • Linux如何将文件或目录打成rpm包? -- fpm打包详解
  • docker容器快速安装启动ES
  • Hylicos - MINI2440 - 中断控制
  • kotlin $ (字符串模版)的使用
  • yarn的安装及使用教程
  • C# wpf利用Clip属性实现截屏框
  • C++11手撕线程池 call_once 单例模式 Singleton / condition_variable 与其使用场景
  • 一文(10图)了解Cornerstone3D核心概念(万字总结附导图)
  • 【Emotion】 自动驾驶最近面试总结与反思
  • 【深度学习】BasicSR训练过程记录,如何使用BasicSR训练GAN
  • elastic search入门
  • 为什么 HTTPS 协议能保障数据传输的安全性?
  • 【Unity学习笔记】New Input System 部分源码和测试用例补充
  • [nginx文档翻译系列] 控制nginx
  • 2019.2.20 c++ 知识梳理
  • 2019年如何成为全栈工程师?
  • Android单元测试 - 几个重要问题
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • ES6之路之模块详解
  • iOS 颜色设置看我就够了
  • JAVA并发编程--1.基础概念
  • Joomla 2.x, 3.x useful code cheatsheet
  • js数组之filter
  • laravel5.5 视图共享数据
  • PAT A1050
  • Promise面试题2实现异步串行执行
  • Spring Boot MyBatis配置多种数据库
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 理解在java “”i=i++;”所发生的事情
  • 力扣(LeetCode)22
  • 判断客户端类型,Android,iOS,PC
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 让你的分享飞起来——极光推出社会化分享组件
  • 手写双向链表LinkedList的几个常用功能
  • ​secrets --- 生成管理密码的安全随机数​
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (04)odoo视图操作
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (办公)springboot配置aop处理请求.
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (五)c52学习之旅-静态数码管
  • (正则)提取页面里的img标签
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • .Net CF下精确的计时器
  • .net refrector
  • .NET 常见的偏门问题
  • .net 调用php,php 调用.net com组件 --
  • .net6使用Sejil可视化日志
  • .Net中ListT 泛型转成DataTable、DataSet
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...