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

news2024/11/25 22:44:56

个人主页: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 -> int
    cnt++;
    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, wfd
    if (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. 关闭不需要的fd
        close(pipefd[0]);

        SubProcessWrite(pipefd[1]);
        close(pipefd[1]);
        exit(0);
    }

    std::cout << "父进程关闭不需要的fd了, 准备收消息了" << std::endl;
    sleep(1);
    //父进程 --- read
    //3. 关闭不需要的fd
    close(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.coloradmin.cn/o/2221204.html

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

相关文章

强心剂!EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断

强心剂&#xff01;EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断 目录 强心剂&#xff01;EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断效果一览基本介绍程序设计参考资料 效果一览 基本介绍 EEMD-MPE-KPCA-LSTM(集合经验模态分解-多尺…

无人机电机损耗!

一、电机损耗类型 机械损耗&#xff1a; 主要由于电机的旋转部件&#xff08;如转子、轴承等&#xff09;在运转过程中产生的摩擦和磨损。 长时间运行或不当维护可能加剧这种损耗。 电气损耗&#xff1a; 包括电阻损耗、铁芯损耗和杂散损耗等。 这些损耗主要由电流通过电…

Golang | Leetcode Golang题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; var (temp []intans [][]int )func findSubsequences(nums []int) [][]int {ans [][]int{}dfs(0, math.MinInt32, nums)return ans }func dfs(cur, last int, nums []int) {if cur len(nums) {if len(temp) > 2 {t : make([]int, len(…

未来AI的学习能力会达到怎样的水平?

​ 大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 AI工具集1&#xff1a;大厂AI工具【共2…

Leetcode—192. 统计词频【中等】(Shell)

2024每日刷题&#xff08;188&#xff09; Leetcode—192. 统计词频 实现代码 # Read from the file words.txt and output the word frequency list to stdout. cat words.txt | tr -s \n | sort | uniq -c | sort -nr | awk {print $2, $1}运行结果 之后我会持续更新&…

学习threejs,通过THREE.Raycaster给模型绑定点击事件

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.Raycaster光线投射概…

DirectX11:Position Based Fluid

前言 这是我本科毕业设计项目&#xff0c;使用DirectX11实现一个基于PBD的流体模拟仿真&#xff0c;同时也算是补了Games101的大作业了。 阅读本文假设你对以下内容比较熟悉&#xff1a; 流体模拟&#xff1a;Smoothed Particle Hydrodynamics 流体模拟&#xff1a;Neighbor…

UNIX网络编程-传输层

概述 传输层主要包括&#xff1a;TCP、UDP、SCTP&#xff08;流控制传输协议&#xff09;&#xff01; 绝大多数客户端/服务器网络应用都使用TCP/UDP。SCTP是一个较新的协议&#xff0c;最初设计用于跨因特网传输电话信令。 这些传输协议都转而使用网络协议IP&#xff1a;或是…

windows中命令行批处理脚本学习

目录 一 基础知识二 常见命令1. 输出 echo2. 注释 rem .... %...% :: goto if (10) ()3. 变量 set4. 获取参数 %数字 %*5. 退出 exit6. 复制 copy7.读取输出文件内容 type8. 帮助 命令xxx /?9.等待当前命令运行结束后,才执行下一条命令 call10. 修改字体编码 chcp11. 特殊变量…

集合框架16:HashMap的使用

视频链接&#xff1a;13.35 HashMap使用&#xff08;1&#xff09;_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p35 1.创建Student类&#xff0c;添加无参构造…

UML之用例图详解

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 零、什么是用例图 用例图&#xff08;Use Case Diagram&#xff09;是UML中一种重要的图表类型&#xff0c;它主要用于描述系统的功能性需求&am…

Java使用HttpClient5实现发送HTTP请求

Java 实现发送 HTTP 请求&#xff0c;系列文章&#xff1a; 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、HttpClient5 的介绍 HttpClient5 是 Apache HttpComponents 项目…

文件处理新纪元:微信小程序的‘快递员’与‘整理师’

嗨&#xff0c;我是中二青年阿佑&#xff0c;今天阿佑将带领大家如何通过巧妙的文件处理功能&#xff0c;让用户体验从‘杂乱无章’到‘井井有条’的转变&#xff01; 文章目录 微信小程序的文件处理文件上传&#xff1a;小程序的“快递服务”文件下载&#xff1a;小程序的“超…

植物大战僵尸杂交版游戏分享

植物大战僵尸杂交版游戏下载&#xff1a;夸克网盘分享 无捆绑之类的隐形消费&#xff0c;下载即玩

vue3 解决背景图与窗口留有间隙的问题

需要实现一个登录界面&#xff0c;login.vue的代码如下&#xff1a; <script> import { ref } from vue;export default {setup() {return {};}, }; </script><template><div id"login-container" class"login-container"><di…

Taro构建的H5页面路由切换返回上一页存在白屏页面过渡

目录 项目背景&#xff1a;Taro与Hybrid开发问题描述&#xff1a;白屏现象可能的原因包括&#xff1a; 解决方案解决后的效果图 其他优化方案可参考&#xff1a; 项目背景&#xff1a;Taro与Hybrid开发 项目使用Taro框架同时开发微信小程序和H5页面&#xff0c;其中H5页面被嵌…

Nodes 节点

Goto Tree List 树列表 Nodes 节点 Tree List 节点是组织成树状层次结构的数据行。 Add New Nodes 添加新节点 如果 Tree List 具有数据源&#xff0c;则会自动生成节点&#xff08;TreeListNode 类对象&#xff09;。要在未绑定模式下添加节点&#xff0c;请调用“树列表设…

【K8S系列】Kubernetes Pod节点Pending状态及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 Pending 表示 Pod 已被创建&#xff0c;但尚未被调度到节点上&#xff0c;或者已调度到节点上但容器尚未开始运行。这一状态常常是因为某些条件未满足&#xff0c;导致 Pod 无法正常启动。以下是对 Pending 状态的详细分析及解决方案…

自由学习记录(12)

综合实践 2D的Shape&#xff0c;Tilemap都要导包的&#xff0c;编辑器也要导包&#xff0c;。。和2d沾边的可能3d都要主动导包 应该综合的去运用&#xff0c;不见得Tilemap就很万能&#xff0c;如果要做什么顶方块的有交互反应的物体&#xff0c; 那直接拖Sprite会更方便一些…