如何创建管道——pipe

举报
yd_221104950 发表于 2020/12/03 01:34:46 2020/12/03
【摘要】 我们有一个这样的场景:主程序希望与子程序进行通信。我们用pipe()函数为他们创建管道,这样他们就可以通过管道来通信了。 为了演示方便我们只考虑: 子程序把数据发送到父进程中来的情况。因此要用管道把子进程的标准输出和父进程的标准输入连接起来 我们将用pipe()函数建立管道。每当我们打开数据流时,它都会加入描述符表。pipe()函数也是如此,它创建两条相连的数据流,并...

我们有一个这样的场景:主程序希望与子程序进行通信。我们用pipe()函数为他们创建管道,这样他们就可以通过管道来通信了。
为了演示方便我们只考虑:
子程序把数据发送到父进程中来的情况。因此要用管道把子进程的标准输出和父进程的标准输入连接起来

我们将用pipe()函数建立管道。每当我们打开数据流时,它都会加入描述符表。pipe()函数也是如此,它创建两条相连的数据流,并把它们加到描述符表中,然后只要我们往其中一条数据流中写数据,就可以从另一条数据流中读取。pipe()在描述符表中创建这两项时,会把它们的文件描述符存放在一个包含两个元素的数组中。

int fd[2];  //文件描述符将保存在fd数组中。
if(pipe(fd) == -1){ fprintf(stderr,"Can't create the pipe!"); exit(2);
} 

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

pipe()函数创建了管道,并返回了两个描述符:fd[0]用来从管道读数据,fd[1]用来向管道写数据。我们将在父、子进程中使用这两个描述符。

我们通过一个实例来说明:
aaa.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void open_url(char *url);
int main(){ int fd[2]; if(pipe(fd) == -1){ //创建管道,并把文件描述符保存在fd[0]和fd[1]中。 fprintf(stderr,"%s\n","Can't create the pipe!"); exit(1); } pid_t pid = fork(); if(pid == -1){ fprintf(stderr,"%s\n","Can't fork process!"); exit(2); } if(!pid){ //能进这里,表示已在子进程中 dup2(fd[1],1); //把标准输出设为管道写入端 close(fd[0]); //因为子进程不会去读取管道,因此将读取端关闭 if(execl("bbb","bbb",NULL) == -1){ fprintf(stderr,"%s\n","Can't open subprocess"); exit(1); } } //从这开始是属于父进程的 dup2(fd[0],0);  //把标准输入设为管道读取端 close(fd[1]);//因为父进程不会向子进程发数据,无需写入管道,因此将写入端关闭 char line[255]; while(fgets(line,255,stdin)){ open_url(line+1); } return 0;
}
void open_url(char *url){ char launch[255]; //sprintf(launch,"cmd /c start %s",url);  //在windows上打开网页 // system(launch); sprintf(launch,"x-www-browser '%s' &",url);//在Linux上打开网页 system(launch); //sprintf(launch,"open '%s'",url); //在Mac上打开网页 //system(launch);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

bbb.c

#include <stdio.h>
int main(){ char line[255]; fgets(line,255,stdin); printf("%s",line); return 0;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

编译运行:

~/Desktop/MyC$ gcc aaa.c -o aaa
~/Desktop/MyC$ gcc bbb.c -o bbb
~/Desktop/MyC$ ./aaa
|

  
 
  • 1
  • 2
  • 3
  • 4

查进程信息:

~$ ps -ef
UID PID  PPID  C STIME TTY TIME CMD
wong 17525  5363  0 17:47 pts/1 00:00:00 ./aaa
wong 17526 17525  0 17:47 pts/1 00:00:00 bbb

  
 
  • 1
  • 2
  • 3
  • 4

输入地址按回车:

~/Desktop/MyC$ ./aaa
http://www.baidu.com

  
 
  • 1
  • 2

接着就会打开浏览器。

管道是文件吗?这不一定。这取决于操作系统创建管道的方式,一般来说用pipe()创建的管道都不是文件。而创建基于文件的管道,通常叫做有名管道或FIFO文件(先进先出文件)。

因为基于文件的管道有名字,所以两个进程只要知道管道的名字也能用它来通信,即使它们是非父子进程关系。要使用有名管道,可以通过使用mkfifo()系统调用来实现。如果不是通过文件来实现管道的话,那通常是用内存,数据写到内存某个位置,然后再从另一个位置读取。

如果试图读取一个空的管道,也不会发生错误,因为程序会等待管道中出现东西。

父进程是如何知道子进程结束的?
其实,当子进程结束时,管道会关闭。fgets()将会收到EOF(End Of File,文件结束符) ,于是fgets()函数返回0,循环就结束了。

另外,管道只能单向通信。但是可以通过创建两个管道,一个从父进程连接到子进程,另一个从子进程连接到父进程来实现双向通信 。

谢谢阅读!

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/87627832

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。