管道

管道

  简单地说,管道可以将一个进程的输出作为另一个进程的输入:
管道
在shell中,一般用符号’ | '表示管道,如:gcc --help | wc -l,相当于将gcc的帮助手册作为wc的输入,wc将统计出该手册的行数并作为最后的输出。

  实际上可以Linux仍然坚持着一切皆是文件的原则,将管道也视为文件。所以上面的过程也可以理解为,'gcc --help’将其输出写入到了一个叫管道的文件,而’wc -l’又从这个管道文件中读取数据作为自己的输入。

  在一个C程序中,可以由父进程创建一个管道再fork一个子进程,然后通过这个管道实现父进程与子进程的通信。按照管道也是文件的思路,管道也将有文件描述符,并且可以调用write及read函数。

数据在进程间流动时是可以经过多个管道的,如 ’ A | B | C | D ’ 。

创建一个管道

  管道可以通过 pipe 函数创建。

#include <unistd.h>

int pipe(int fd[2]);

在前面仅仅提到管道也是文件的情况下可能会理解为管道就是一个文件,实际应该理解为一个管道是两个文件。

  调用 pipe 函数后,若成功创建一个管道将返回0,并且将两个文件描述符保存在数组 fd[ ] 中。其中fd[0]用于读管道中的内容,而fd[1]用于将内容写入管道。所以在这种情况下笔记开头部分的图最好画成这样子:
管道

父进程与子进程都是可以引用两个文件描述符的,所以两个进程都可以对 fd[0]读、对fd[1]写 。
需要注意的是,父进程创建管道要在fork子进程之前,负责子进程将无法得知管道的文件描述符。

关闭不必要的管道端口

  从前面所述可知,父子进程都拥有两个文件描述符。在通常情况下,管道中数据的流向是确定的,比如从父进程流向子进程。此时,父进程用不到读文件而子进程用不到写文件,因此应让父进程关闭管道的读端而子进程关闭写端。

这里的关闭并不是指关闭文件,而是将进程与文件之间的联系断开。只需要进程中close掉相应的文件描述符即可。

  关闭相应读写端之后如下图所示:
管道

让管道的某一端成为标准输入/输出

  在Linux有很多工具都是默认将标准输入/输出作为自己的输入/输出,所以要用管道和这些工具进行通信首先要把管道的端口编程标准输入/输出。

  要做到这一点可以使用dup函数,这里只以修改为标准输入为例。标准输入所对应的文件描述符为0,首先用close将0描述符对应文件关闭,然后调用dup函数复制fd[0]描述符,此时0描述符和fd[0]描述符将指向同一个文件,然后再将fd[0]描述符关闭(因为fd[0]已经是多余的了,但不关闭也是可以的),此时管道的读端口就是标准输入了。

  和Linux工具通信的具体做法如下:

  1. 父进程建立管道
  2. fork出子进程
  3. 父子进程各自关闭不必要的管道端口
  4. 利用dup将管道的读端修改为标准输入
  5. 子进程exec某个Linux工具,如less分页工具
  6. 最后实现了父进程与less的通信