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

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.mrgr.cn/news/56369.html

相关文章:

  • Torch模型导入
  • DirectDraw和Direct3D的区别
  • [Luogu 4630] APIO2018 铁人两项(广义圆方树)
  • SpringBoot实现mysql多数据源配置(Springboot+Mybatis)
  • BurpSuite渗透工具的简单使用
  • 【Power Query】List.Select 筛选列表
  • Oracle分区表改造(三):通过分区交换和分裂改造为分区表
  • 基于Multisim电子配料秤电路设计(含仿真和报告)
  • MySQL数据库的高可用
  • 应对 .DevicData-X-XXXXXXXX 勒索病毒:防御与恢复策略
  • 07 实战:视频捕获
  • 高效HR运营,10佳系统指南
  • 支付域——交易系统设计
  • 防火墙是什么?科普为保护应用层而生的可靠工具
  • MacPro M3无法运行minikube 和 docker
  • 衣柜是在乳胶漆之前装还是之后装好呢?
  • 《使用Gin框架构建分布式应用》阅读笔记:p108-p126
  • Java程序设计:spring boot(7)——数据访问操作
  • 10月22日
  • 虚拟机风格
  • Java笔试06
  • 【功能安全】汽车功能安全个人认证证书
  • 搭建AlexNet神经网络,并搭建自己的图像分类训练和测试的模板,模板通用!!!均有详细注释。
  • 平衡二叉树最全代码
  • 使用SpringBoot自定义注解+AOP+redisson锁来实现防接口幂等性重复提交
  • Java避坑案例 - 消除代码重复_模板方法与工厂模式的最佳实践