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

从零开始学习管道:管道程序的优化和文件描述符继承问题

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容管道后续的完善,以及解决管道继承多个文件描述符的问题

文章目录

    • 1.管道程序的再优化
      • 1.1void ctrlprocess函数
      • 1.2EndPoint类
      • 1.3RecailmTask函数
      • 1.4子进程继承父进程文件描述符问题的解决

1.管道程序的再优化

1.1void ctrlprocess函数

分三步:

  • 确定任务
  • 确定执行任务的子进程.轮询式的
  • 执行任务
void ctrlprocess(const vector<EndPoint>& end_points)
{// 2.写成自动化,也可以搞成交互式的int cnt = 0;while (true){//1.确定任务int command = ShowBoard();if(command == 3) break;if(command < 0 || command > 2) continue;//2.确定执行任务的子进程.轮询式的int child = cnt++;cnt %= end_points.size();cout << "选择了进程:" << end_points[child].name() <<"| 处理任务:" << command << endl;//3.执行任务write(end_points[child]._write_fd, &command, sizeof(command));sleep(1);}
}

1.2EndPoint类

  • 增加static成员number:用于统计子进程个数
  • 增加string processname:用于存储进程的名字
  • 增加name函数:用于打印子进程的名字
// 先描述
class EndPoint
{
private:static int number;
public:pid_t _child;  // 子进程pidint _write_fd; // 对应的文件描述符string processname;
public:// 构造EndPoint(int id, int fd): _child(id), _write_fd(fd){char namebuffer[64];snprintf(namebuffer, sizeof(namebuffer), "process-%d[%d:%d]", number++, id, fd);processname = namebuffer;}string name() const {return processname;}// 析构~EndPoint(){}
};

1.3RecailmTask函数

用于子进程的回收:

  • ①关闭写描述符:根据前面讲的父进程关闭了管道对应的写描述符之后,子进程也就退出了
  • ②回收子进程:waitpid对应的函数进行回收!

方案一:两种操作分开执行

void RecailmTask(const vector<EndPoint>& end_points)
{//1.关闭写描述符for(int i = 0; i < end_points.size(); ++i) close(end_points[i]._write_fd);cout << "父进程让所有的进程退出了" << endl;sleep(5);//2.回收子进程for(int i = 0; i < end_points.size(); ++i) waitpid(end_points[i]._child, nullptr, 0);sleep(5);
}

运行结果:

子进程在父进程的操控下,正常退出了

image-20231126215109563

1.4子进程继承父进程文件描述符问题的解决

但是当我们把这两个过程合并的时候问题出现了:

然后就卡住了

void RecailmTask(const vector<EndPoint>& end_points)
{//1.关闭写描述符for(int i = 0; i < end_points.size(); ++i) {close(end_points[i]._write_fd);waitpid(end_points[i]._child, nullptr, 0);}cout << "父进程让所有的进程退出了" << endl;sleep(5);
}

image-20231126220145423

这是为什么呢?其实我们想一想,父进程在创建子进程的时候子进程也会把父进程的文件描述符也会拷贝一份

image-20231126221535592

所以除了第一个子进程当我们父进程关闭对应的写端的时候子进程不会关闭,原因是其他的子进程也继承了对应的写端的文件描述符。所以写端并没有完全关闭,所以这时候父进程去等待回收子进程的时候就会一直在等待,造成了程序卡住的状态!那么我们怎么解决呢?

方案一:反着顺序关闭写端

void RecailmTask(const vector<EndPoint>& end_points)
{//1.关闭写描述符for(int end = end_points.size() - 1; end >= 0; --end) {close(end_points[end]._write_fd);waitpid(end_points[end]._child, nullptr, 0);}cout << "父进程让所有的进程退出了" << endl;sleep(5);
}

image-20231126222855900

可是我们这种办法只是解决了表象,我们没有解决的根本的情况,我们只是让程序可以正常的关闭,但是子进程的那种继承父进程管道的文件描述符的问题还是没有解决,并且这也是有一定不安全的情况在里面的,因为管道不是一对一的情况了,变成了多对一的情况,可能会造成其他的子进程向其他管道中错误写入的问题:

所以我们我解决这个问题,这个问题我们应该在创建子进程管道的时候就解决好!

思路:我们每创建一个子进程,把其对应的文件描述符存储在一个vector内部,然后再创建第二个子进程的时候,遍历vector的时候,将他们依次关掉即可!

void creatProcesses(vector<EndPoint> &end_points)
{//用于存储文件描述符vector<int>fds;// 1.先进行构建控制结构,父进程写,子进程读for (int i = 0; i < gnum; ++i){// 1.1创建管道int pipefd[2] = {0};int ret = pipe(pipefd);assert(ret == 0); // 0正常 -1不正常(void)ret;// 1.2创建进程pid_t id = fork();assert(id != -1);if (id == 0){//关闭不必要的描述符for(auto& fd : fds) close(fd);// 子进程// 1.3关闭不要的fdclose(pipefd[1]);// 我们期望,所有的子进程读取“指令”的时候,都从标准输入读取// 1.3.1所以我们进行输入重定向dup2(pipefd[0], 0);// 1.3.2子进程开始等待获取命令WaitCommend();close(pipefd[0]);exit(0);}// 父进程// 1.3关闭不要的fdclose(pipefd[0]);// 1.4将新的子进程和他的管道写端构建对象。end_points.push_back(EndPoint(id, pipefd[1]));fds.push_back(pipefd[1]);}
}

运行结果:

image-20231126224519572


到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

相关文章:

  • gitee推荐-1Panel
  • 搜索百度可以直接生成代码拉
  • 【广州华锐互动】节约用水VR互动教育:身临其境体验水资源的珍贵!
  • ubuntu/vscode下的c/c++开发之-CMake语法与练习
  • Git多库多账号本地SSH连接配置方法
  • gitea仓库镜像同步至gitlab
  • 阿里云跨账号建立局域网
  • 深入理解RC4加密算法
  • 2023亚太杯数学建模A题思路分析 - 采果机器人的图像识别技术
  • 线程基本方法
  • 使用Pytorch从零开始构建Conditional PixelCNN
  • C#异常处理-throw语句
  • 软著项目推荐 深度学习 python opencv 火焰检测识别 火灾检测
  • C++值常用集合算法
  • 简易键值对文本解析
  • 【391天】每日项目总结系列128(2018.03.03)
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Java-详解HashMap
  • js中forEach回调同异步问题
  • maya建模与骨骼动画快速实现人工鱼
  • mysql innodb 索引使用指南
  • Spark学习笔记之相关记录
  • Twitter赢在开放,三年创造奇迹
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • vue.js框架原理浅析
  • vuex 学习笔记 01
  • 前端面试题总结
  • 设计模式走一遍---观察者模式
  • 线上 python http server profile 实践
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 责任链模式的两种实现
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (ros//EnvironmentVariables)ros环境变量
  • (八)Spring源码解析:Spring MVC
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (南京观海微电子)——COF介绍
  • (推荐)叮当——中文语音对话机器人
  • (一)基于IDEA的JAVA基础10
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .net core 连接数据库,通过数据库生成Modell
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .net 怎么循环得到数组里的值_关于js数组
  • .Net 中Partitioner static与dynamic的性能对比
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .NetCore项目nginx发布
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .net知识和学习方法系列(二十一)CLR-枚举