Linux进程管道

发布时间:2026/10/8 4:44:18

Linux进程管道 一匿名管道1.创建匿名管道只能用于同祖先进程之间通信将子进程的数据传给父进程#include iostream #include string #include unistd.h #include sys/types.h #include cstdio #include cstdlib #include cstring using namespace std; int main() { //在克隆进程前先创建一个管道 int pipefd[2];//装管道读写两端的fd的数组 int ret pipe(pipefd); if (ret -1) { perror(pipe); exit(0); } //复制进程 pid_t pid fork(); //父进程 if (pid 0) { char buf[1024] { 0 }; read(pipefd[0], buf, sizeof(buf)); printf(parrent recieve %s,pid:%d\n, buf, getpid()); } //子进程 else if (pid 0) { sleep(2); string msg Hello from child process; // .c_str() 获取 const char*适配系统调用write write(pipefd[1], msg.c_str(), msg.size()); } return 0; }0s主线程执行pipe()创建匿名管道 → 执行fork()分裂两个进程。子进程继承父进程全部文件描述符此时父子进程同时持有管道读端、写端两份 fd分裂瞬间父进程立刻进入 if (pid0) 分支执行read(pipefd[0], buf, sizeof(buf));当前管道是空的管道阻塞特性生效 →父进程被操作系统挂起、休眠阻塞不再继续运行等待管道内出现数据分裂瞬间子进程进入 else if (pid0) 分支执行sleep(1)→ 子进程主动睡眠 1 秒。第 2 秒到来子进程 sleep 结束执行write(pipefd[1], str, strlen(str));将字符串写入管道内核缓冲区数据写入管道的一瞬间内核发现管道有数据唤醒被阻塞的父进程 read 函数父进程read读取到缓冲区数据填充到 buf 数组执行 printf 打印内容父子进程各自执行到return 0进程正常退出2.匿名管道双向传输#include iostream #include string #include unistd.h #include sys/types.h #include cstdio #include cstdlib #include cstring using namespace std; int main() { //在克隆进程前先创建一个管道 int pipefd[2];//装管道读写两端的fd的数组 int ret pipe(pipefd); if (ret -1) { perror(pipe); exit(0); } //复制进程 pid_t pid fork(); //父进程 if (pid 0) { char buf[1024] { 0 }; while (1) { read(pipefd[0], buf, sizeof(buf)); printf(parrent recieve %s,pid:%d\n, buf, getpid()); string msg Hello from parent process; // .c_str() 获取 const char*适配系统调用write write(pipefd[1], msg.c_str(), msg.size()); sleep(2); } } //子进程 else if (pid 0) { char buf[1024] { 0 }; while (1) { string msg Hello from child process; // .c_str() 获取 const char*适配系统调用write write(pipefd[1], msg.c_str(), msg.size()); sleep(2); read(pipefd[0], buf, sizeof(buf)); printf(childen recieve %s,pid:%d\n, buf, getpid()); } } return 0; }但是不建议这么写容易混乱匿名管道是单向通信 管道规则pipefd[0] 读端pipefd[1] 写端数据流只能写端 → 读端天然半双工不能直接双向收发。现在父子共用同一条管道双向读写会出现严重冲突 父子都持有pipefd[0] 读pipefd[1] 写双方互相抢管道极易阻塞、消息错乱、死锁。2.实现 ps aux | grep xxx 父子进程间通信子进程 ps aux, 子进程结束后将数据发送给父进程父进程获取到数据过滤pipe()execlp()子进程将标准输出 stdout_fileno 重定向到管道的写端。 dup2#include unistd.h #include sys/types.h #include stdio.h #include stdlib.h #include string.h #include wait.h int main() { //创建管道 int fd[2]; int ret pipe(fd); if (ret -1) { perror(pipe); exit(0); } //复制进程 pid_t pid fork(); if (pid 0) { close(fd[1]);//关闭写端 char buf[1024] { 0 }; while (read(fd[0], buf, sizeof(buf)) 0) { printf(%s, buf); memset(buf, 0, 1024); } } else if (pid 0) { close(fd[0]); dup2(fd[1], STDOUT_FILENO);//重定向将输出到终端重定向到管道输入端 execlp(ps, ps, aux, NULL); perror(execlp); exit(0); } else { exit(0); } }execlp:本质上就是用 C 语言代码100% 还原了在终端Terminal里敲下ps aux并按下回车的整个过程你在终端敲下命令的第一个词ps第 1 个参数ps你敲ps时Shell 会去系统路径PATH里找ps这个软件在哪里。代码里的第一个ps就是让execlp去寻找这个可执行文件。操作系统启动ps进程第 2 个参数ps当程序跑起来时它需要知道“我自己叫什么”。终端会自动把ps传给程序的argv[0]代码里也必须显式传入这个ps。你在ps后面空格跟的选项aux第 3 个参数aux终端里空格后面的内容就是参数。代码里的aux对应ps程序的argv[1]告诉ps“我要查看**所有用户a/u/x**的进程信息”。你在终端按下了回车Enter第 4 个参数NULL回车代表“我的命令敲完了请执行”。代码里的NULL就是那个“回车”告诉内核“参数全部传完了可以开始执行了”。如果你在终端换一个命令代码怎么变理解了这种对应关系后你可以轻松把代码改成执行其他命令例子 1在终端敲grep bash终端grep bash代码execlp(grep, grep, bash, NULL);例子 2在终端敲ls -l -a /home终端ls -l -a /home代码execlp(ls, ls, -l, -a, /home, NULL);总结execlp(ps, ps, aux, NULL)并不是什么神奇的固定咒语它就是在 C 语言层面调用ps aux指令的标准写法。3.管道读写总结1读管道行为有数据时返回值read返回实际读到的字节数无数据时写端全关闭read返回0类似文件结束符EOF写端未全关read会阻塞等待直到有数据可读才返回2写管道行为读端全关闭信号机制进程会收到SIGPIPE信号默认处理通常导致进程异常终止读端未全关管道已满write会阻塞等待可用空间管道未满write返回实际写入的字节数设置管道非阻塞int flags fcntl(fd[0], F_GETFL); // 获取原来的flag flags | O_NONBLOCK; // 修改flag的值 fcntl(fd[0], F_SETFL, flags); // 设置新的flagfcntl(fd[0], F_GETFL);获取fd[0]当前的文件状态标志Flags。flags | O_NONBLOCK;通过按位或运算在保留原有标志的基础上叠加非阻塞标志O_NONBLOCK。fcntl(fd[0], F_SETFL, flags);将修改后的标志写回给fd[0]使其生效。阻塞vs非阻塞管道状态默认阻塞模式设置非阻塞后O_NONBLOCK管道中有数据正常读取数据并返回读取字节数正常读取数据并返回读取字节数管道为空且写端还开着进程卡死/挂起一直等到有数据写入为止立即返回-1同时设置全局变量errno EAGAIN或EWOULDBLOCK管道为空且写端全关闭返回0代表读取到 EOF 文件末尾返回0代表读取到 EOF 文件末尾二.有名管道fifo1 和匿名管道区别文件系统表现:FIFO作为特殊文件存在文件系统中匿名管道无文件系统表现**内容存储机制:FIFO内容实际存储在内核内存缓冲区匿名管道同样使用内核缓冲区**生命周期:FIFO文件在进程退出后仍保留匿名管道随进程结束而销毁**命名与访问:FIFO通过路径名访问匿名管道通过继承的文件描述符访问2 有名管道创建命令函数阻塞只读阻塞一个进程以只读方式打开管道时会阻塞直到另一个进程以只写方式打开该管道只写阻塞一个进程以只写方式打开管道时会阻塞直到另一个进程以只读方式打开该管道有名管道实现跨进程通信mkfifo.c#include sys/types.h #include sys/stat.h #include stdio.h #include unistd.h #include stdlib.h #include fcntl.h #includestring.h int main() { //判断文件是否存在 int ret access(test, F_OK); //不存在就创建一个 if (ret -1) { printf(管道不存在创建一个); ret mkfifo(test, 0664); if (ret -1) { perror(mkfifo); exit(0); } } return 0; }write.c#include sys/types.h #include sys/stat.h #include stdio.h #include unistd.h #include stdlib.h #include fcntl.h #includestring.h int main() { //判断文件是否存在 int ret access(test, F_OK); //不存在就创建一个 if (ret -1) { printf(管道不存在创建一个); ret mkfifo(test, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //以只写的方式打开管道 int fd open(test, O_WRONLY);//只有读端开启才会写入否则阻塞 if (fd -1) { perror(open); exit(0); } //写数据 for (int i 0; i 100; i) { char buf[1024]; sprintf(buf, hello %d\n, i); write(fd, buf, sizeof(buf)); printf(write %s\n, buf); sleep(1); } close(fd); return 0; }read.c#include sys/types.h #include sys/stat.h #include stdio.h #include unistd.h #include stdlib.h #include fcntl.h #includestring.h int main() { //打开管道文件 int fd open(test, O_RDONLY); if (fd -1) { perror(open); exit(0); } while (1) { char buf[1024] {0}; int lenread(fd, buf, sizeof(buf)); if (len 0) { printf(写端断开连接了...\n); break; } printf(read %s\n, buf); } close(fd); return 0; }有名管道互传信息一次只能发送一条a发一条b收一条b发一条a收一条//chatA #include stdio.h #include unistd.h #include sys/types.h #include sys/stat.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h int main() { //判断管道fifo1存不存在 int ret access(fifo1, F_OK); if (ret -1) { printf(无管道1创建一个\n); ret mkfifo(fifo1, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //判断管道fifo2存不存在 ret access(fifo2, F_OK); if (ret -1) { printf(无管道2创建一个\n); ret mkfifo(fifo2, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //打开两个管道 int fdw open(fifo1, O_WRONLY); int fdr open(fifo2, O_RDONLY); char buf[1024]; //开始循环读写 while (1) { //写管道 memset(buf, 0, sizeof(buf)); printf(chatA 输入\n); fgets(buf, sizeof(buf), stdin); retwrite(fdw, buf, sizeof(buf)); if (ret -1) { perror(write); exit(0); } //读管道 memset(buf, 0, sizeof(buf)); retread(fdr, buf, sizeof(buf)); printf(收到B%s, buf); if (ret -1) { perror(read); exit(0); } } return 0; }//chatB #include stdio.h #include unistd.h #include sys/types.h #include sys/stat.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h int main() { //判断管道fifo1存不存在 int ret access(fifo1, F_OK); if (ret -1) { printf(无管道1创建一个\n); ret mkfifo(fifo1, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //判断管道fifo2存不存在 ret access(fifo2, F_OK); if (ret -1) { printf(无管道2创建一个\n); ret mkfifo(fifo2, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //打开两个管道 int fdr open(fifo1, O_RDONLY); int fdw open(fifo2, O_WRONLY); char buf[1024]; //开始循环读写 while (1) { //读管道 memset(buf, 0, sizeof(buf)); ret read(fdr, buf, sizeof(buf)); printf(收到A%s, buf); if (ret -1) { perror(read); exit(0); } //写管道 memset(buf, 0, sizeof(buf)); printf(chatB 输入\n); fgets(buf, sizeof(buf), stdin); ret write(fdw, buf, sizeof(buf)); if (ret -1) { perror(write); exit(0); } } return 0; }但是这个只能解决一条一条发如果同时一个进程发送两条消息就会堵塞另一个进程无显示当Aw后Br然后B就进入代码W部分AB都是W阻塞有名管道互传信息改良可以一次发送多条改良将写管道和读管道分为两个进程父子进程每个进程只负责读/写//chata #include stdio.h #include unistd.h #include sys/types.h #include sys/stat.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h int main() { //判断管道fifo1存不存在 int ret access(fifo1, F_OK); if (ret -1) { printf(no fifo1set one\n); ret mkfifo(fifo1, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //判断管道fifo2存不存在 ret access(fifo2, F_OK); if (ret -1) { printf(no fifo2set one\n); ret mkfifo(fifo2, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //克隆子进程 pid_t pid fork(); char buf[1024]; //父进程写 if (pid 0) { int fdw open(fifo1, O_WRONLY); while (1) { //写管道 memset(buf, 0, sizeof(buf)); printf(chatA input\n); fgets(buf, sizeof(buf), stdin); ret write(fdw, buf, sizeof(buf)); if (ret -1) { perror(write); exit(0); } } } //子进程读 else if (pid 0) { int fdr open(fifo2, O_RDONLY); while (1) { //读管道 memset(buf, 0, sizeof(buf)); ret read(fdr, buf, sizeof(buf)); printf(recieveB%s, buf); if (ret0) { perror(read); exit(0); } } } return 0; }//chatb #include stdio.h #include unistd.h #include sys/types.h #include sys/stat.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h int main() { //判断管道fifo1存不存在 int ret access(fifo1, F_OK); if (ret -1) { printf(no fifo1set one\n); ret mkfifo(fifo1, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //判断管道fifo2存不存在 ret access(fifo2, F_OK); if (ret -1) { printf(no fifo2set one\n); ret mkfifo(fifo2, 0664); if (ret -1) { perror(mkfifo); exit(0); } } //克隆子进程 pid_t pid fork(); char buf[1024]; //父进程读 if (pid 0) { int fdr open(fifo1, O_RDONLY); while (1) { //读管道 memset(buf, 0, sizeof(buf)); ret read(fdr, buf, sizeof(buf)); printf(recieveA%s, buf); if (ret 0) { perror(read); exit(0); } } } else if (pid 0) { int fdw open(fifo2, O_WRONLY); while (1) { //写管道 memset(buf, 0, sizeof(buf)); printf(chatB input\n); fgets(buf, sizeof(buf), stdin); ret write(fdw, buf, sizeof(buf)); if (ret -1) { perror(write); exit(0); } } } return 0; }
延伸阅读

更多相关文章

2026/10/8 4:45:20

Es删除索引数据相关命令总结

es http请求方式es 可以通过客户端api集成操作,也可以发起 http请求对集群进行操作(1)curl发起请求curl 使用curl -u "用户名:密码" -XPOST -H -d Content-Type: application/json "http://es....." 可以命令行执行&a…

2026/10/7 9:50:43

注塑机明明有空,工单为什么还是开不了?排产前先查这4个条件

注塑厂排产时,经常会遇到一种看起来很矛盾的情况: 排产表上显示某台注塑机明天有空,PMC便把新订单安排过去。等到车间准备开工时,却发现模具还在其他机台上,原料没有准备好,或者上一张订单刚做完深色产品&a…

2026/10/9 3:39:39

重要时期安全保障服务:从战时态到闭环值守的全流程解析

简介:面向政企单位信息安全负责人、项目集成人员与方案编写者的重要时期安全保障服务技术方案。文档以重大政治经济时期的业务连续性为着眼点,完整覆盖防护准备、监控预警、应急处置与复盘改进等环节,并明确对标ISO/IEC 27001、GB/T 20984等标…

2026/10/9 3:39:39

随机森林分类建模实战:从原理到代码与调参

随机森林(Random Forest,RF)算是我个人最爱给刚接触分类建模的朋友推荐的第一选择。市面上的教程往往只教一句“调包就行”,但真到项目里,数据怎么喂、参数怎么试、结果怎么解释,处处有细节。这篇实战笔记就…

2026/10/9 3:39:39

架构自动化转换避坑指南:从规则设计到质量门禁

1. 先说结论:自动化转换工具到底能不能用这几年我前后经手过好几个架构改造项目,从老系统搬迁到新技术栈,从单体拆微服务,到统一通信协议,几乎每一次都会遇到同一个问题:要不要用自动化转换工具。我的答案已…

2026/10/9 3:39:39

LSTM诗歌生成实战:拆解自动写诗工程与训练代码

简介:自动写诗实验包聚焦AI与自然语言处理结合的应用场景,面向具备一定Python基础、想上手文本生成项目的学习者或开发者。资源共18个文件,压缩包约23.83MB,除Python源码与编译后的pyc文件外,还包含实验指导书、实验报…

2026/10/9 3:39:39

图像去噪算法从入门到实战:空间域、小波与深度学习全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 3:34:39

垃圾代码分级规范:从代码评审的争吵到技术债量化管理

代码评审会上,我见过最多次数的场景就是:有人甩出一句“这段代码写得跟垃圾一样”,然后整个讨论组就炸了。写代码的人不服气,反问“哪里垃圾了,你给我说清楚”,评审的人憋了半天,只能说“反正就…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 0:04:27

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略当数万字的学位论文初稿经历开题、实验、问卷与多轮文献梳理最终成形时,绝大多数研究生都会面临一道全新的形式审查关卡:AIGC 疑似度排查。在高校毕业审核流程中,盲审前的文本检测通…

2026/10/9 0:04:27

食堂节能改造源头工厂,商用厨房设备焕新方案广受好评

商用厨房作为餐饮经营、单位供餐的核心后勤阵地,其设备配置、动线规划与运维体系直接决定后厨作业效率、运营成本与合规性。从基础的灶具、制冷存储设备,到油烟净化、水处理等配套系统,每一个环节的合理性都与食品安全、能耗管控、消防安全挂…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑