Linux系统基础-进程间通信(3)_模拟实现匿名管道

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

Linux系统基础-进程间通信(3)_模拟实现匿名和命名管道

收录于专栏[Linux学习]
本专栏旨在分享学习Linux的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 模拟实现匿名管道 

实现的功能 :

实现方法 :

1. 获取消息的函数

2. 子进程写入模块

3. 父进程读取模块

4. 主函数

2. 效果展示: 


1. 模拟实现匿名管道 

实现的功能 :

通过管道实现进程间通信, 父子进程之间的数据传输是单向的 (父进程只读, 子进程只写) 

实现方法 :

1. 获取消息的函数

功能 : 生成一个包含消息ID 和当前进程ID 的字符串, 作为子进程发送给父进程的消息的一部分.

std::string getOtherMessage()
{static int cnt = 0;std::string messageid = std::to_string(cnt); //stoi -> string -> intcnt++;pid_t self_id = getpid();std::string stringpid = std::to_string(self_id);std::string message = "messageid:";message += messageid;message += "my pid is : ";message += stringpid;return message;
}

 这个函数的核心目的是生成一个包含唯一消息ID和当前进程ID的字符串, 用于进程间通信或调试输出, 通过使用静态变量cnt, 每次调用该函数时都能生成唯一的消息ID, 而通过getpid()函数获取当前进程的PID, 可以帮助识别消息的来源进程. 

这种机制在多进程编程中非常有用, 特别是在使用管道, 信号量或其他IPC (进程间通信) 机制时.

2. 子进程写入模块

功能 : 子进程通过管道向父进程写入

void SubProcessWrite(int wfd)
{std::string message = "father, I am your son process!";while(true){std::cerr << "++++++++++++++++++++++++++++++++++++++" << std::endl;std::string info = message + getOtherMessage(); //这条消息, 就是我们子进程发给父进程的消息write(wfd, info.c_str(), info.size());//写入管道的时候, 没有写入\0, 有没有必要std::cerr << info << std::endl;}std::cout << "child quit ..." << std::endl;
}

关于\0问题 : 

\0 字符: 在 C++ 中,字符串是以 null 字符 \0 结尾的。write() 函数写入的是指定长度的字节(由 info.size() 决定),因此不需要显式写入 \0。当父进程读取这条消息时,读取的字节数是已知的,所以不需要依赖字符串的结尾。

使用标准错误流打印的原因:

1. 区分标准输出和错误输出

std::cerr 是标准错误流,用于输出错误信息或调试信息,而 std::cout 是标准输出流,用于正常的程序输出。

将调试信息或错误信息发送到 std::cerr,可以清晰地将这些信息与正常的程序输出区分开来。这在复杂程序中尤其重要,因为它有助于在审查日志时快速识别问题。

2. 输出缓冲机制

std::cout 通常是带缓冲的输出流,可能会因为缓冲区未满而延迟输出信息。这在某些情况下可能导致调试信息不能立即看到。

相比之下,std::cerr 是不带缓冲的,意味着信息会立即被输出。这在调试过程中非常有用,因为你希望能即时看到任何错误或状态信息,而不是等到缓冲区填满。

3. 调试和监控

在开发和调试过程中,使用 std::cerr 输出调试信息(如进程状态、错误信息等)是个常见做法,可以帮助开发者实时监控程序的运行状态。

如果程序崩溃或出现异常,std::cerr 中的输出通常会被保留下来,而 std::cout 的输出可能因为程序的崩溃而丢失。

总结: 

功能: SubProcessWrite 函数的主要目的是通过管道向父进程发送消息。它构造了一条包含固定消息和动态消息的字符串,并通过管道不断地发送。

3. 父进程读取模块

功能 : 父进程从管道中读取子进程发送的消息

void FatherProcessRead(int rfd)
{char inbuffer[size];while(true){sleep(2);std::cout << "------------------------------------" << std::endl;size_t n = read(rfd, inbuffer, sizeof(inbuffer) - 1);//sizeof(inbuffer) -> strlen(inbuffer)if(n > 0){inbuffer[n] = 0; // == '\0'std::cout << inbuffer << std::endl;}else if(n == 0){//如果read返回值是0, 表示写端直接关闭了, 我们读到了文件的结尾std::cout << "client quit father get return val: " << n << "father quit too!" << std::endl;break;}else if(n < 0){std::cerr << "read error" << std::endl;break;}}
}

FatherProcessRead 函数的主要目的是从子进程通过管道读取数据并输出到标准输出。它处理三种读取结果:正常读取、读到文件结束和读取错误 

4. 主函数

功能 : 管理进程的创建和管道的操作

int main()
{// 1. 创建管道int pipefd[2];int n = pipe(pipefd); // 输出型参数, rfd, wfdif (n != 0){std::cerr << "errno: " << errno << ": " << "errstring : " << strerror(errno) << std::endl;return 1;}//pipefd[0] -> 0 -> r(读) pipefd[1] -> 1 -> w(写)std::cout << "pipefd[0]: " << pipefd[0] << ", pipefd[1]: " << pipefd[1] << std::endl;sleep(1);//2. 创建子进程pid_t id = fork();if(id == 0){std::cout << "子进程关闭不需要的fd了, 准备发消息了" << std::endl;sleep(1);//子进程 --- write//3. 关闭不需要的fdclose(pipefd[0]);SubProcessWrite(pipefd[1]);close(pipefd[1]);exit(0);}std::cout << "父进程关闭不需要的fd了, 准备收消息了" << std::endl;sleep(1);//父进程 --- read//3. 关闭不需要的fdclose(pipefd[1]);FatherProcessRead(pipefd[0]);std::cout << "5S, father close rfd" << std::endl;sleep(5);close(pipefd[0]);int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){std::cout << "wait child process done, exit sig : " << (status&0x7f) << std::endl;std::cout << "wait child process done, exit code(ign) : " << ((status >> 8)& 0xFF) << std::endl; }return 0;
}

pipefd: 这是一个整数数组,pipefd[0] 用于读取数据(读端),pipefd[1] 用于写入数据(写端)。

pipe(): 创建管道,如果成功则返回 0,并将读端和写端的文件描述符分别存储在 pipefd[0] 和 pipefd[1] 中。

fork(): 创建一个子进程。返回值 id 为 0 表示当前进程是子进程。 

调用 SubProcessWrite(pipefd[1]),假设这个函数负责将数据写入管道。

写入完成后,关闭写端 pipefd[1],并通过 exit(0) 结束子进程。

父进程关闭管道的写端 pipefd[1],因为它只需要读取数据。

调用 FatherProcessRead(pipefd[0]),假设这个函数负责从管道中读取数据。

等待子进程结束并获取状态 :

waitpid(): 等待子进程结束并获取其状态。

状态处理:

status & 0x7f 用于获取子进程的退出信号。

((status >> 8) & 0xFF) 用于获取子进程的退出码。

如果成功,输出子进程的退出信号和退出码。

2. 效果展示: 

将我们的程序编译运行 : 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/56240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker入门(三)自定义部署docker镜像

docker系列d​​​​​​​docker入门&#xff08;一&#xff09;安装及镜像命令_docker国内源-CSDN博客文章浏览阅读1.5k次&#xff0c;点赞44次&#xff0c;收藏12次。注意&#xff1a;是强依赖Linux环境&#xff0c;即便在windows上部署Docker其本质也都是先安装一个虚拟机&…

SpringBoot3.x和OCR构建车牌识别系统

本专题旨在展示 OCR 技术与 SpringBoot3.x 框架结合的广泛应用。我们会深入探讨它在医疗、金融、教育、交通、零售、公安等多个领域的现实应用。每个应用场景都会提供详细的实例、面临问题的分析与解决策略&#xff0c;以帮助您深入理解 OCR 技术在实践中的关键作用。让我们一同…

糖果——差分约束 + 正环判定及其优化(手搓栈 + 标记法)

题目 思考 这里转为判定负环可以是可以&#xff0c;但是不能用超级源点了&#xff08;改为把节点全部压入&#xff09;&#xff0c;因为按照题目条件&#xff0c;建立的应该是各个节点指向超级源点的有向边&#xff0c;这显然破坏了超级源点的功能 代码 #include <bits/st…

【数据结构与算法】Java中的基本数据结构:数组、链表、树、图、散列表等。

探索Java集合框架&#xff1a;数据结构的精髓与应用 摘要&#xff1a; 在本文中&#xff0c;我们将深入探讨Java集合框架中的核心数据结构&#xff0c;包括数组、链表、树、图、散列表、栈、队列、集合、映射和优先队列。通过分析每种数据结构的实现原理和特点&#xff0c;你将…

ArcGIS002:软件自定义设置

摘要&#xff1a;本文详细介绍安装arcgis10.2后软件自定义设置内容&#xff0c;包括工具条的启用、扩展模块的启用、如何加载项管理器、快捷键设置、样式管理器的使用以及软件常规设置。 一、工具条的启用 依次点击菜单栏【自定义】->【工具条】&#xff0c;根据工作需求勾…

某ai gpt的bug

某ai gpt的bug 背景 遇到了一个奇怪的现象&#xff1a; 输入内容 2024-10-21 10:09:31,052 ERROR o.a.j.t.JMeterThread: Test failed! java.lang.IllegalArgumentException:输出结果

使用 PyTorch 构建 LSTM 股票价格预测模型

目录 引言准备工作1. 训练模型&#xff08;train.py&#xff09;2. 模型定义&#xff08;model.py&#xff09;3. 测试模型和可视化&#xff08;test.py&#xff09;使用说明模型调整结论 引言 在金融领域&#xff0c;股票价格预测是一个重要且具有挑战性的任务。随着深度学习…

即时通讯增加Redis渠道

情况说明 在本地和服务器分别启动im服务&#xff0c;当本地发送消息时&#xff0c;会发现服务器上并没有收到消息 初版im只支持单机版&#xff0c;不支持分布式的情况。此次针对该情况对项目进行优化,文档中贴出的代码非完整代码&#xff0c;可自行查看参考资料[2] 代码结构调…

电机PWM控制中的定时器配置与信号极性对电机转向的影响分析

文章目录 摘要关键词1. 引言2. PWM控制与定时器配置3. 实验与问题描述3.1 定时器配置代码分析3.2 问题分析 4. 解决方案5. 实验结果6. 结论 电机PWM控制中的定时器配置与信号极性对电机转向的影响分析 摘要 PWM&#xff08;脉宽调制&#xff09;信号是控制电机速度与方向的常…

Docker安装ocserv教程(效果极佳)

本章教程,介绍如何在Debain系统上安装ocserv。安装方式是使用Docker方式部署。 一、安装Docker curl -sSL https://file.ewbang.com/docker/debian/install_docker.sh -o install_docker.sh && bash install_docker.sh二、拉取镜像 docker pull tommylau/ocserv

数据库-01MYSQL-002MySQL5.7错误连接未释放导致数据库连接失败

**报错信息&#xff1a;** 信息&#xff1a; 11276:无法创建数据库<NAMSE_89>的连接:jdbc:mysql://xx.xx.xx.xx:3306/v_report xxxx_user 23246:无法创建数据库<NAMSE_89>的连接:jdbc:mysql://xx.xx.xx.xx:3306/v_report xnzb_user null, message from server: &qu…

工作窃取(Work-Stealing)是什么?

工作窃取&#xff08;Work-Stealing&#xff09;是什么&#xff1f; 工作窃取是一种并行任务调度算法&#xff0c;用于最大化 CPU 资源利用率&#xff0c;特别适合任务分解递归式的并发场景。其核心思想是&#xff1a;当某个线程完成了自己分配的任务后&#xff0c;如果其他线…

wsl下将exfat/NTFS类型的硬盘准换为ext4

问题 wsl 直接读 windows 文件速度慢如果 wsl 挂载一个 ext4 的硬盘会快些 方法 查看物理盘号 首先&#xff0c;你需要获取外置硬盘的磁盘标识符。你可以使用 Windows 的 diskpart 工具来获取&#xff1a; 打开命令提示符&#xff08;以管理员身份运行&#xff09;。 输入…

Jsoup在Java中:解析京东网站数据

对于电商网站如京东来说&#xff0c;其页面上的数据包含了丰富的商业洞察。对于开发者而言&#xff0c;能够从这些网站中提取有价值的信息&#xff0c;进行分析和应用&#xff0c;无疑是一项重要的技能。本文将介绍如何使用Java中的Jsoup库来解析京东网站的数据。 Jsoup简介 …

银河麒麟(debian)下安装postgresql、postgis

1、安装postgresql、postgis sudo apt update sudo apt install postgresql postgresql-contrib sudo apt install postgis postgresql-12-postgis-32、创建一个使用postgis的数据库 sudo -i -u postgres #postgres管理员用户createdb gisdb #创建新的gisdb数据库 psql -d gi…

c++日常积累

在 C 中&#xff0c;可以直接将 int 类型的值赋值给 bool 类型。C 会自动进行类型转换&#xff0c;任何非零的 int 值都会被转换为 true&#xff0c;而 0 会被转换为 false。 QDialog 有一个 finished(int) 信号&#xff0c;该信号在对话框关闭时发出&#xff0c;并传递一个整…

Linux部署redis保姆级教程

一、版本说明 Redis版本号(本文的版本号是6.2.12)的第二位如果是偶数,代表稳定版本,如果是奇数,代表非稳定版本。 所有历史版本下载地址:Index of /releases/ 二、基于压缩包安装(推荐) 2.1安装依赖 2.1.1安装gcc: yum -y install gcc 2.1.2验证gcc是否安装成功:(…

Vue使用OnlyOffice预览文档方案

服务器安装OnlyOffice ~~ 找后台或运维OfficePreview.vue <template><div id"officePreview"></div> </template><script setup> import { onMounted } from "vue";const props defineProps({previewUrl: {type: String,d…

Linux--多路转接之epoll

上一篇:Linux–多路转接之select epoll epoll 是 Linux 下多路复用 I/O 接口 select/poll 的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。它是 Linux 下多路复用 API 的一个选择&#xff0c;相比 select 和 poll&#xff0c…

通过梧桐数据库分析客户价值

在现代商业环境中&#xff0c;对客户价值的分析至关重要。通过分析客户的消费行为&#xff0c;企业可以更好地理解其客户群体&#xff0c;并据此制定更有效的市场策略。本文将介绍如何使用SQL查询来分析客户价值&#xff0c;包括计算每个客户的总消费金额&#xff0c;并根据这些…