4. “匿名”管道和命名管道

前面管道的例子不但非常有趣,而且还形象地描述了链接的概念,接下来我们就回过头来在研究一下它。当您在命令行模式下使用管道时, shell 创建管道并以如下方式对其进行操作:管道之前的命令写入管道,而管道之后的命令从管道里读取信息。所有管道,不论匿名的 (像在 shell 里使用的)还是命名的 (见下文) 都是 FIFO(先进先出)的。虽然我们已经提供了如何在 shell 中使用它们的例子,但是为了说明问题,我们来看另外一个例子。

$ ls -d /proc/[0-9] | head -5
/proc/1/
/proc/2/
/proc/3/
/proc/4/
/proc/5/

您可能没有注意到(因为它发生得太快以至于您很难观察到它),在这个例子中,写入是可以堵塞的。这意味着当 ls 命令写入管道的时候,堵塞会持续到管道另一端的进程从管道中读取数据。为了形象化说明这个效果,我们可以建立命名管道。它不像在 shell 中使用的那样,而是有名字的(即它们是被链接的,而在 shell 中它们并不是)[5]。命名管道由命令 mkfifo 创建:

$ mkfifo a_pipe
$ ls -il
total 0
169 prw-rw-r--  1 zhang zhang 0 Aug  6 19:37 a_pipe|
  #
  # 您可以看出链接计数为 1,而且上述输出显示
  # 该文件为一管道('p')。
  #
  # 此处您同样可以使用 ln:
  #
$ ln a_pipe the_same_pipe
$ ls -il
total 0
169 prw-rw-r--  2 zhang zhang 0 Aug  6 19:37 a_pipe|
169 prw-rw-r--  2 zhang zhang 0 Aug  6 19:37 the_same_pipe|
$ ls -d /proc/[0-9] >a_pipe
  #
  # 该进程将被阻塞,因为在管道另一边并没有什么去读。
  # 按下 Control Z 挂起该进程 ...
  #
[1]+  Stopped                 ls -F --show-control-chars --color=auto -d /proc/[0-9] >a_pipe
  #
  # ... 并将其转入后台运行:
  #
$ bg
[1]+ ls -F --show-control-chars --color=auto -d /proc/[0-9] >a_pipe &
  #
  # 现在从管道中读取 ...
  #
$ head -5 <the_same_pipe
  #
  # ... 写入进程终止
  #
/proc/1/
/proc/2/
/proc/3/
/proc/4/
/proc/5/
[1]+  Done                    ls -F --show-control-chars --color=auto -d /proc/[0-9] >a_pipe
$

类似地,读取也是可以堵塞的。如果以相反的顺序执行以上命令,我们将看到 head 被阻塞并等待一些过程提供读取的数据。

$ head -5 <a_pipe 
# 
# 程序阻塞,将其挂起:C-z 
# 
[1]+  Stopped                 head -5 <a_pipe 
# 
# 将其转入后台运行 ...  
# 
$ bg 
[1]+ head -5 <a_pipe &
# 
# ... 并给它些食物 :) 
# 
$ ls -d /proc/[0-9] >the_same_pipe 
/proc/1/ 
/proc/2/ 
/proc/3/ 
/proc/4/ 
/proc/5/ 
[1]+  Done                    head -5 <a_pipe
$

您也可以在前面的例子里看到一个我们并不愿意引入的效果: ls 命令在head 命令夺取控制权之前就终止了。这样的结果就是您立即回到了提示符状态,但是 head 会在之后执行,您只能返回到提示符状态之后看到它的结果。



[5] 两种管道还有别的区别,但那些都已经超出本书所要讨论的范围。