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

Linux - 进程间通信(3)

目录

3、解决遗留BUG -- 边关闭信道边回收进程

1)解决方案

2)两种方法相比较

4、命名管道

1)理解命名管道

2)创建命名管道

a. 命令行指令

b. 系统调用方法

3)代码实现命名管道

构建类进行封装命名管道:

构造和析构:

读取管道、写入管道:

server.cc (读端):

client.cc(写端):

效果:

4)疑点解决

写端未来,读端open调用阻塞

读端关闭,写端继续写入

5)完整代码

namedPipe.hpp:

server.cc:

client.cc:


3、解决遗留BUG -- 边关闭信道边回收进程

1)解决方案

我们仍然想用上述方法进行管道和子进程的回收

-- 则需要解决子进程所继承的父进程遗留的多余wfd,我们在每次创建子进程时,遍历所有之前的信道,关闭掉wfd即可,就不会出现,多个wfd指向一个管道

2)两种方法相比较

退一个回收一个:

先全部退出,再进行等待回收:

4、命名管道

1)理解命名管道

命名:该管道有名字,因为该文件有路径,有路径必有文件名

管道:依旧是一个内存级的基于文件进行通信的通信方案

属性、操作、文件内核缓冲区同一个文件的都是差不多的,因此不用再创建一份,操作系统不做浪费时间和空间的事情

我们怎么保证两个毫不相关的进程打开了同一个文件呢??
每一个文件,都有文件路径(唯一性)

2)创建命名管道

a. 命令行指令

一个进程(echo)向命名管道里面输入数据

一个进程(cat)向命名管道里面读取数据

这样就实现了两个毫不相关的进行之间的通信

创建了三个窗口,一个一直向管道输入,一个一直读取,一个手动检测管道大小

但是我们可以看到管道文件(myfifo)的大小一直显示0

因为 FIFO0 文件虽存在于文件系统中,但其内容都存放在内存里,不会将通信数据刷新到磁盘中,所以在磁盘上显示的文件大小始终为0

b. 系统调用方法

使用mkfifo即可创建管道文件

使用unlink即可删除一个管道文件(当然,rm也可以删除)

3)代码实现命名管道

创建两个.cc文件分别模拟两个进程,一个进行发送,一个进行读取

通过一个CreateNamedPipe和一个RemoveNamedPipe就可以实现对管道生命周期的管理

当然,我们管理管道的声明周期时,肯定是将创建和删除交给同一个文件去做比较好,因为它清楚什么时候去删除合适

这里我们让发送的那方去管理管道的生命周期

构建类进行封装命名管道:

我们需要创建管道的路径(共同路径)、创建管道的身份、管道的文件描述符

class NamedPipe
{
private:const std::string _fifo_path;int _id;int _fd;
};
构造和析构:
class NamedPipe
{
public:NamedPipe(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;}}~NamedPipe(){sleep(5);if (_id == Creater){int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}std::cout << "creater remove named pipe" << std::endl;}if(_fd != DefaultFd) close(_fd);}private:const std::string _fifo_path;int _id;int _fd;
};
读取管道、写入管道:
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 (_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 ReadNamedPipe(std::string *out){char buffer[BaseSize];int n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0; // '\0'*out = buffer;}return n;}int WriteNamedPipe(const std::string &in){return write(_fd, in.c_str(), in.size());}~NamedPipe(){sleep(5);if (_id == Creater){int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}std::cout << "creater remove 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);// 对于读端而言,如果我们打开了文件,但是写还没有来,我们会阻塞在open调中,直到对方打开// --> 一种变向的进程同步if (fifo.OpenForRead()){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"// client -- 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;
}
效果:

实现了两个进程(无父子关系)之间的通信

4)疑点解决

写端未来,读端open调用阻塞

读端关闭,写端继续写入

5)完整代码

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>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 4096class 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 (_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 ReadNamedPipe(std::string *out){char buffer[BaseSize];int n = read(_fd, buffer, sizeof(buffer));if(n > 0){buffer[n] = 0; // '\0'*out = buffer;}return n;}int WriteNamedPipe(const std::string &in){return write(_fd, in.c_str(), in.size());}~NamedPipe(){sleep(5);if (_id == Creater){int res = unlink(_fifo_path.c_str());if (res != 0){perror("unlink");}std::cout << "creater remove 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);// 对于读端而言,如果我们打开了文件,但是写还没有来,我们会阻塞在open调中,直到对方打开// --> 一种变向的进程同步if (fifo.OpenForRead()){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"// client -- 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;
}
http://www.lryc.cn/news/530584.html

相关文章:

  • 3、C#基于.net framework的应用开发实战编程 - 实现(三、三) - 编程手把手系列文章...
  • C++编程语言:抽象机制:泛型编程(Bjarne Stroustrup)
  • Python面试宝典13 | Python 变量作用域,从入门到精通
  • 基于最近邻数据进行分类
  • DeepSeek V3 vs R1:大模型技术路径的“瑞士军刀“与“手术刀“进化
  • 一、TensorFlow的建模流程
  • 指导初学者使用Anaconda运行GitHub上One - DM项目的步骤
  • 7层还是4层?网络模型又为什么要分层?
  • C++:抽象类习题
  • C++ 泛型编程指南02 (模板参数的类型推导)
  • 音视频入门基础:RTP专题(5)——FFmpeg源码中,解析SDP的实现
  • 计算机网络 应用层 笔记 (电子邮件系统,SMTP,POP3,MIME,IMAP,万维网,HTTP,html)
  • 【视频+图文详解】HTML基础3-html常用标签
  • FreeRTOS学习 --- 消息队列
  • PHP If...Else 语句详解
  • pytorch使用SVM实现文本分类
  • 安卓(android)读取手机通讯录【Android移动开发基础案例教程(第2版)黑马程序员】
  • 【Qt】常用的容器
  • 基于UKF-IMM无迹卡尔曼滤波与交互式多模型的轨迹跟踪算法matlab仿真,对比EKF-IMM和UKF
  • 分布式事务组件Seata简介与使用,搭配Nacos统一管理服务端和客户端配置
  • JavaScript常用的内置构造函数
  • 25寒假算法刷题 | Day1 | LeetCode 240. 搜索二维矩阵 II,148. 排序链表
  • MQTT知识
  • 【机器学习与数据挖掘实战】案例11:基于灰色预测和SVR的企业所得税预测分析
  • 新一代搜索引擎,是 ES 的15倍?
  • 使用 Context API 管理临时状态,避免 Redux/Zustand 的持久化陷阱
  • PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统
  • word2vec 实战应用介绍
  • C# 操作符重载对象详解
  • python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理