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

【linux操作系统】进程间通信--管道

进程间通信的本质是让不同的进程看到同一份资源。而管道就在此处作同一份资源。

管道是用来进程间通信的一种方式。可以将管道理解为一个内核级缓冲区,用来暂时存储进程间的数据。linux下一切皆文件,通过write,read等系统调用来访问管道中的数据。

管道的特点

1.单项通信,即只允许一方说话。

2.管道是面向字节流的。简单来说就是输入进管道的东西不一定会一次性读出来,自己可以规定读取的边界。

3.管道的生命周期随进程,本质是文件。

4.管道通信对于多进程而言自带同步互斥

匿名管道

匿名管道本质上来说就是访问这一处的资源不需要路径+文件名。只能被用来没有血缘关系间的进程间的通信。

创建方法

pipefd[0],pipefd[1]分别为读端和写端。

int pipefd[2]; pipe(pipefd);

父子进程利用匿名管道通信有两个约束点,同步和互斥,同步的意思是访问管道要有顺序性,互斥的意思是同一时间只能有一个人访问资源,会有四种情况出现。(以子写父读为例)

1.子进程写的慢,父进程阻塞,给写机会。

2.父进程读的慢,写满后子进程阻塞,给读机会。

3.读端在读,写端关闭,读完管道内容后read返回0。

4.写端在写,读端关闭,os直接杀死子进程。

下面给出一个进程池的demo

#include <iostream> #include <unistd.h> #include <string.h> #include <cstdio> #include <vector> #include <functional> #include <time.h> #include <stdlib.h> #include <sys/wait.h> //////////////////////////////////////////////////////////////任务 typedef void (*mytask)(void); // 函数指针 void Sendmsg() { std::cout << getpid() << " : 发送信息!!" << std::endl; return; } void Fflush() { std::cout << getpid() << " : 刷新刷新!!" << std::endl; return; } void Sleep() { std::cout << getpid() << " : 休眠一下!!" << std::endl; return; } void OpenAi() { std::cout << getpid() << " : 打开ai软件!!" << std::endl; return; } void Close() { std::cout << getpid() << " : 关机 !!!" << std::endl; return; } // 函数指针数组 mytask cb_task[5] = {Sendmsg, Fflush, Sleep, OpenAi, Close}; //////////////////////////////////////////////////////////////进程池 enum { OK = 0, pipe_err, fork_err }; const int gprocessnum = 5; // 五个子进程 // typedef std::function<void (int)> task; using task_t = std::function<void(int)>; // 给函数类型重命名,这个函数要返回值是void,参数是int void Dotask(int fd) { // do task while (true) { int intask = 0; size_t s = read(fd, &intask, sizeof(intask)); if (s == sizeof(intask)) { if (intask <= 4 && intask >= 0) std::cout << intask << " "; cb_task[intask](); } else if (s == 0) { break; } } close(fd); exit(0); // sleep(100); } // 进程池 class proccess_pool { public: proccess_pool() { } ~proccess_pool() { } void Init(task_t cb) { creat_process_channel(cb); } void Debug() { for (auto &ch : channels) { ch.print_info(); } } void run() { int count = 5; while (count) { // 选择一个任务 int intask = select_task(); // 选择一个进程 int intpro = select_process(); // 将任务发送给进程 send_task2process(intask, intpro); sleep(1); count--; } } void quit() { for (auto &ch1 : channels) { ch1.channel_close(); } for (auto &ch2 : channels) { ch2.pro_wait(); } return; } private: class Channel { public: Channel(int wfd, pid_t id) : _wfd(wfd), _id(id) { _sub_name = "channel_name : " + std::to_string(_id); } ~Channel() { } void print_info() { printf("写端编号:%d, 子进程:%d, %s\n", _wfd, _id, _sub_name.c_str()); } int Re_wfd() { return _wfd; } void channel_close() { printf("关闭wfd:%d\n", _wfd); close(_wfd); } void pro_wait() { printf("子进程回收:%d\n", _id); sleep(1); pid_t rid = waitpid(_id, nullptr, 0); (void)rid; } private: int _wfd; // 记录打开的管道的写端 pid_t _id; // 以及对应的子进程的pid std::string _sub_name; }; void creat_process_channel(task_t cb) { for (int i = 0; i < gprocessnum; i++) { // 创建管道 int pipefd[2] = {0}; int n = pipe(pipefd); if (n < 0) { std::cerr << "pipe errro" << std::endl; exit(pipe_err); // 不用return不用返回 } // 创建子进程 pid_t id = fork(); if (id < 0) { std::cerr << "fork errro" << std::endl; exit(fork_err); } else if (id == 0) { close(pipefd[1]); // 子进程读取,关闭写端 // Dotask(pipefd[0]);子进程只能干一件事 // 关闭子进程多余的写端 for (auto &ch : channels) { close(ch.Re_wfd()); } cb(pipefd[0]); // 回调函数 cb就是Dotask 以后可以通过初始化来给子进程传递不同的任务 exit(0); } else { close(pipefd[0]); // Channel ch(pipefd[1], id); // channels.push_back(ch); printf("子进程创建成功!!pid : %d\n", id); sleep(1); channels.emplace_back(pipefd[1], id); } } } // 采用轮询的方式选择进程 int select_process() { static int n = 0; // 这行命令只会执行一次 n++; n %= 5; return n; } int select_task() { srand(time(nullptr)); int index = rand() % 5; // 将会有五个任务 return index; // 返回一个0-4的整数 } void Write(int a, int b) { // 第几个进程就对应着第几个通道嘛 int n = write(channels[b].Re_wfd(), &a, sizeof(a)); // 规定一次写4字节 (void)n; } // 给子进程发送任务 void send_task2process(int a, int b) // a是任务编号,b是进程编号 { // 这是父进程写的过程 // 写过去的也只会是任务的编号 Write(a, b); return; } std::vector<Channel> channels; }; int main() { proccess_pool pp; pp.Init(Dotask); pp.Debug(); pp.run(); pp.quit(); return 0; }

命名管道

命名管道与匿名管道相对,命名管道有自己的路径+文件名用于两个完全不相关的进程间发生通信。文件相关的核心结构只会存在一份。管道文件里面的内容只会存在于缓冲区,不会刷新到磁盘上。

使用int mkfifo(const char *filename,mode_t mode)函数创建命名管道。第一个参数表示管道名称,第二个参数表示管道权限。同样是用write,read等系统调用对管道进行访问。

下面是一段利用命名管道通信的demo。

#include <iostream> #include <cstring> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <errno.h> #include <cerrno> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #define SIZE 1024 const char *name = "./pipe"; #define READ_OPEN 1 // 0001 #define WRITE_OPEN 2 // 0010 class namepipe { public: namepipe(const std::string name_pipe = name, mode_t mode = 0666) : _pipe_name(name_pipe), _mode(mode), _fd(-1) { } void Build() { umask(0); if (Isexit()) { std::cout << "管道已经存在!!!" << std::endl; return; } // 文件不存在就创建 int p = mkfifo(_pipe_name.c_str(), _mode); if (p < 0) { std::cerr << "mkfifo error : " << strerror(errno) << " errno " << errno << std::endl; } else { std::cout << "管道创建成功!!!" << std::endl; } } void Open(int flag) { // 读方式打开 if (flag & READ_OPEN) { std::cout << "开始打开文件" << std::endl; _fd = open(_pipe_name.c_str(), O_RDONLY); if (_fd < 0) { std::cerr << "open error : " << strerror(errno) << " errno " << errno << std::endl; std::cout << "文件打开失败!!" << std::endl; exit(-1); } char outbuffer[SIZE]; while (true) { ssize_t s = read(_fd, outbuffer, sizeof(outbuffer) - 1); if (s > 0) { std::cout << outbuffer << std::endl; } else if (s == 0) { close(_fd); break; } } } // 以写方式打开 else if (flag & WRITE_OPEN) { _fd = open(_pipe_name.c_str(), O_WRONLY); if (_fd < 0) { std::cout << "文件打开失败!!" << std::endl; exit(-1); } int cnt = 10; while (cnt--) { std::cout << "Please write your msg :"; std::string inbuffer; std::getline(std::cin, inbuffer); int n = write(_fd, inbuffer.c_str(), inbuffer.size()); // std::cout << inbuffer << std::endl; } close(_fd); } else { } } void Delete() { if (!Isexit()) { return; } // 存在就删除 int n = unlink(_pipe_name.c_str()); if (n == 0) { std::cout << "成功删除" << std::endl; } else { std::cerr << "unlink error : " << strerror(errno) << " errno " << errno << std::endl; } } ~namepipe() { } private: const std::string _pipe_name; mode_t _mode; pid_t _fd; bool Isexit(const char *name = "./pipe") { struct stat buffer; int n = stat(name, &buffer); if (n == 0) return true; else return false; } };

完。。。

http://www.cnnetsun.cn/news/1301833.html

相关文章:

  • Phi-3-vision-128k-instruct 代码理解实战:解析 C++ 项目结构图
  • Youtu-VL-4B-Instruct在内容审核场景的应用:自动识别违规图片信息
  • Ubuntu20.04下高效部署Eigen3.3.7与模板类Sophus、Ceres的完整指南
  • EagleEye实操手册:Streamlit可视化大屏+毫秒级检测结果实时渲染
  • TypeScript 一日速通指南:数据类型全解析与转换指南
  • SpringBoot项目集成Kook Zimage真实幻想Turbo:一键部署AI绘画接口
  • 嘎嘎降AI双引擎驱动是什么?比单引擎降AI效果好在哪
  • Qwen3-14B开源大模型实践:Qwen3-14b_int4_awq在vLLM下支持function calling实测
  • 机器学习周报三十六
  • RMBG-2.0效果实测:复杂背景中人物发丝分割精度达99.2%(CEILab测试集)
  • Qt实战:打造可配置的动态流水连接线组件
  • 热键失灵背后的隐形劫持者:Hotkey Detective技术侦探实战指南
  • 基于大模型与向量数据库的智能客服系统:架构设计与性能优化实战
  • CAN总线节能秘籍:用TJA1145实现智能部分网络(Partial Networking)配置
  • 储能电气——01 锂电池系统工作原理
  • AudioSeal Pixel Studio快速上手:音频水印与数字签名技术融合
  • 思科模拟器实战:从零配置交换机+DHCP+ACL完整实验(附常见错误排查)
  • 基于大语言模型的毕设实战:AI辅助开发全流程避坑指南
  • 计算机组成原理实战:存储器字位扩展的设计与实现
  • RAG在中小企业智能客服中的实战应用:从架构设计到性能优化
  • 开源工具焕新老旧Mac设备:突破系统限制的完整技术指南
  • Chord视频理解工具实现Python爬虫数据智能处理:自动化采集与清洗
  • B端电销拓客的困境:精准度上不去,成本下不来,问题出在哪?要么乱打不对的,要么辛辛苦苦筛选号码,我发现了一个:氪迹科技,法人号码核验筛选,价格离谱:0.003/条
  • Phi-3-vision-128k-instruct快速部署:基于vLLM的低延迟多模态服务搭建
  • IntelliJ IDEA 集成 Codeium:免费AI编程助手的高效实践指南
  • Qwen3-14B开源大模型实战:基于int4 AWQ的低资源文本生成解决方案
  • MGeo地址实体对齐实战:快速解决CRM客户信息重复问题
  • Three.js Shader实战:5步打造动态天空云层效果(附完整代码)
  • 2026年03月15日 星期日 22:44:23 +0800
  • TurboDiffusion避坑指南:Wan2.1模型部署与生成常见问题解答