news 2026/7/13 13:31:41

应用——管道与文件描述符

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
应用——管道与文件描述符

一、管道(Pipe)

1. 基本概念

  • 管道是一种半双工的通信方式,数据只能单向流动。

  • 只能在具有亲缘关系的进程之间使用。

  • 管道本质上是一个内核缓冲区,通过文件描述符进行读写操作。

  • 包括读端fd[0]和写端fd[1]

2. 创建管道

int fd[2]; pipe(fd);

3. 读阻塞(Read Blocking)

  • 如果管道为空,读操作会阻塞,直到有数据写入。

示例代码:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc, char **argv) { int fd[2]={0}; int ret = pipe(fd); if(-1 == ret) { perror("pipe error\n"); return 1; } pid_t pid = fork(); if(pid>0) { close(fd[0]); int i = 3; while(i--) { printf("father 准备数据\n"); sleep(1); } char buf[1024]="hello ,son"; write(fd[1],buf,strlen(buf)); close(fd[1]); } else if(0== pid) { close(fd[1]); char buf[1024]={0}; // 读阻塞 示例 read(fd[0],buf,sizeof(buf)); printf("father say:%s\n",buf); close(fd[0]); } else { perror("fork"); return 1; } return 0; }
  • 子进程调用read时,父进程尚未写入数据,子进程阻塞等待。

  • 父进程睡眠3秒后写入数据,子进程才继续执行。

4. 写阻塞(Write Blocking)

  • 如果管道缓冲区满,写操作会阻塞,直到有空间写入。

示例代码:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(int argc, char **argv) { int fd[2]={0}; int ret = pipe(fd); if(-1 == ret) { perror("pipe error\n"); return 1; } pid_t pid = fork(); if(pid>0) { close(fd[0]); char buf[1024]={0}; memset(buf,'a',sizeof(buf)); int i = 0 ; // 写阻塞 示例 for(i=0;i<65;i++) { write(fd[1],buf,sizeof(buf)); printf("%d\n",i); } close(fd[1]); } else if(0== pid) { close(fd[1]); while(1) { sleep(1); } close(fd[0]); } else { perror("fork"); return 1; } return 0; }
  • 父进程循环写入数据,直到管道满,后续写入会阻塞。

  • 子进程不读数据,导致写端持续阻塞。

5. 管道破裂(Broken Pipe)

  • 当读端关闭,写端继续写入时,会触发SIGPIPE信号,默认行为是终止进程。

示例代码:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { int fd[2] = {0}; int ret = pipe(fd); if (-1 == ret) { perror("pipe error\n"); return 1; } pid_t pid = fork(); if (pid > 0) { close(fd[0]); sleep(3); char buf[1024] = "hello ,son"; //管道破裂 gdb 查看 //Program received signal SIGPIPE, Broken pipe write(fd[1], buf, strlen(buf)); printf("--------------\n"); close(fd[1]); } else if (0 == pid) { close(fd[1]); close(fd[0]); exit(1); } else { perror("fork"); return 1; } return 0; }
  • 子进程关闭了读端,父进程写入数据时触发管道破裂。

6. 读取管道结束(EOF)

  • 当写端关闭,读端读取完所有数据后,read返回0,表示管道结束。

示例代码:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { int fd[2] = {0}; int ret = pipe(fd); if (-1 == ret) { perror("pipe error\n"); return 1; } pid_t pid = fork(); if (pid > 0) { close(fd[0]); char buf[1024] = "hello ,son"; write(fd[1], buf, strlen(buf)); close(fd[1]); exit(0); } else if (0 == pid) { close(fd[1]); sleep(3); while (1) { char buf[1024] = {0}; int ret = read(fd[0], buf, sizeof(buf)); // ret ==0 enf of pipe if(ret<=0) { break; } printf("father say:%s\n", buf); } close(fd[0]); } else { perror("fork"); return 1; } return 0; }
  • 父进程写入数据后关闭写端。

  • 子进程读取数据,当read返回0时,退出循环。

7. 管道复制文件

  • 管道可用于父子进程间传递大量数据,如文件复制。

示例代码:

#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { int fd[2] = {0}; int ret = pipe(fd); if (-1 == ret) { perror("pipe error\n"); return 1; } pid_t pid = fork(); if (pid > 0) { close(fd[0]); int src_fd = open("/home/linux/1.png", O_RDONLY); if (-1 == src_fd) { perror("open src"); return 1; } while (1) { char buf[1024] = {0}; int rd_ret = read(src_fd, buf, sizeof(buf)); if(rd_ret<=0) { break; } write(fd[1], buf, rd_ret); } close(fd[1]); close(src_fd); } else if (0 == pid) { close(fd[1]); int dst_fd = open("2.png",O_WRONLY|O_CREAT|O_TRUNC,0666); if (-1 == dst_fd) { perror("open dst"); return 1; } while(1) { char buf[1024]={0}; int rd_ret = read(fd[0],buf,sizeof(buf)); if(rd_ret<=0) { break; } write(dst_fd,buf,rd_ret); } close(fd[0]); close(dst_fd); } else { perror("fork"); return 1; } return 0; }
  • 父进程读取文件内容,通过管道传递给子进程。

  • 子进程从管道读取数据并写入目标文件。


二、命名管道(FIFO)

1. 基本概念

  • FIFO 是一种命名管道,存在于文件系统中。

  • 可用于无亲缘关系的进程间通信。

  • 使用mkfifo创建 FIFO 文件。

2. 创建 FIFO

mkfifo("myfifo", 0666);

3. 读写阻塞特性

  • 读端先打开:会阻塞,直到写端打开。

  • 写端先打开:会阻塞,直到读端打开。

示例代码:

  • 写端程序

    #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { int ret = mkfifo("myfifo", 0666); if (-1 == ret) { // 如果是文件已存在的错误,程序就继续运行 if (EEXIST == errno) { } else //如果是其他的错误,进程结束 { perror("mkfifo"); return 1; } } // open 会阻塞, //写段先运行 ,写段会等读段出现, // 读段先运行 ,读段会等写段出现, int fd = open("myfifo", O_WRONLY); if (-1 == fd) { perror("open"); return 1; } char buf[] = "hello,friend...\n"; write(fd, buf, strlen(buf) + 1); close(fd); return 0; }
  • 读端程序

    #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> int main(int argc, char **argv) { int ret = mkfifo("myfifo", 0666); if (-1 == ret) { // 如果是文件已存在的错误,程序就继续运行 if (EEXIST == errno) { } else //如果是其他的错误,进程结束 { perror("mkfifo"); return 1; } } // open 会阻塞, //写段先运行 ,写段会等读段出现, // 读段先运行 ,读段会等写段出现, int fd = open("myfifo", O_RDONLY); if (-1 == fd) { perror("open"); return 1; } char buf[1024] = {0}; read(fd, buf, sizeof(buf)); printf("fifo_w:%s\n", buf); close(fd); // remove("myfifo"); return 0; }

4. 错误处理

  • 如果 FIFO 已存在,mkfifo会失败,errnoEEXIST,通常忽略该错误。


三、文件描述符与 FILE* 转换

1.fileno:从 FILE* 获取文件描述符

FILE *fp = fopen("/etc/passwd", "r"); int fd = fileno(fp);

示例代码:

#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { FILE* fp = fopen("/etc/passwd","r"); if(NULL== fp) { perror("fopen"); return 1; } //fgets/fputs // FILE* -> int int fd = fileno(fp); char buf[100]={0}; read(fd,buf,sizeof(buf)-1); printf("%s",buf); fclose(fp); return 0; }
  • 使用fopen打开文件得到FILE*

  • 使用fileno获取对应的文件描述符,用于read等系统调用。

2.fdopen:从文件描述符获取 FILE*

int fd = open("/etc/passwd", O_RDONLY); FILE *fp = fdopen(fd, "r");

示例代码:

#include <stdio.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { int fd = open("/etc/passwd",O_RDONLY); if(-1== fd) { perror("open"); return 1; } //read /write char buf[100]={0}; FILE* fp = fdopen(fd,"r") ; if(NULL == fp) { perror("fdopen"); return 1; } fgets(buf,sizeof(buf),fp); printf("buf :%s",buf); fclose(fp); return 0; }
  • 使用open打开文件得到文件描述符。

  • 使用fdopen转换为FILE*,用于fgets等标准 I/O 函数。


四、总结

通信方式特点适用场景
无名管道半双工、亲缘进程、内存缓冲区父子进程间通信
命名管道有名字、文件系统可见无亲缘关系进程间通信
文件描述符系统调用、低级 I/O直接操作文件或设备
FILE*标准 I/O、带缓冲区高级文本或流式操作

注意事项:

  • 管道通信时,及时关闭不需要的文件描述符。

  • 注意处理 SIGPIPE 信号,避免进程意外终止。

  • FIFO 使用时注意读写端的阻塞与同步。

  • 文件描述符与 FILE* 可以相互转换,便于混合使用系统调用和标准 I/O。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 10:06:52

2026年中专学历考取高薪出纳岗位证书指南大纲

出纳岗位核心技能要求基础财务知识&#xff08;会计原理、票据管理、银行结算&#xff09;办公软件操作&#xff08;Excel函数、财务软件如用友/金蝶&#xff09;法律法规&#xff08;《会计法》《支付结算办法》基础条款&#xff09;适合中专生的高含金量证书推荐初级会计职称…

作者头像 李华
网站建设 2026/7/13 6:36:46

基于java的SpringBoot/SSM+Vue+uniapp的演唱会网上订票系统的详细设计和实现(源码+lw+部署文档+讲解等)

文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言 &#x1f31e;博主介绍&#xff1a;✌全网粉丝15W,CSDN特邀作者、211毕业、高级全…

作者头像 李华
网站建设 2026/7/12 23:42:13

sci文献检索入口及使用指南:高效查找学术资源的关键平台

你是不是也这样&#xff1a;下载的PDF堆满文件夹&#xff0c;想找的时候死活记不住名字&#xff1b;读文献时灵感一闪&#xff0c;回头却找不到记在哪了&#xff1b;写论文时&#xff0c;调整一个引用格式就要折腾半小时…文献管理不是小事&#xff0c;它直接决定了你的研究效率…

作者头像 李华
网站建设 2026/7/14 3:05:49

ai智能搜索文献:高效精准的学术资源检索新方式

你是不是也这样&#xff1a;下载的PDF堆满文件夹&#xff0c;想找的时候死活记不住名字&#xff1b;读文献时灵感一闪&#xff0c;回头却找不到记在哪了&#xff1b;写论文时&#xff0c;调整一个引用格式就要折腾半小时…文献管理不是小事&#xff0c;它直接决定了你的研究效率…

作者头像 李华
网站建设 2026/7/14 5:21:47

如何高效进行文献怎么查:实用方法与技巧指南

你是不是也这样&#xff1a;下载的PDF堆满文件夹&#xff0c;想找的时候死活记不住名字&#xff1b;读文献时灵感一闪&#xff0c;回头却找不到记在哪了&#xff1b;写论文时&#xff0c;调整一个引用格式就要折腾半小时…文献管理不是小事&#xff0c;它直接决定了你的研究效率…

作者头像 李华
网站建设 2026/7/14 2:37:14

文献管理工具考核方案设计与实施要点分析

你是不是也这样&#xff1a;下载的PDF堆满文件夹&#xff0c;想找的时候死活记不住名字&#xff1b;读文献时灵感一闪&#xff0c;回头却找不到记在哪了&#xff1b;写论文时&#xff0c;调整一个引用格式就要折腾半小时…文献管理不是小事&#xff0c;它直接决定了你的研究效率…

作者头像 李华