了解Unix / Linux中的管道命令


16

我有两个简单的程序:AB。 A首先运行,然后B获取“ stdout” A并将其用作其“ stdin”。假设我使用的是GNU / Linux操作系统,最简单的方法是:

./A | ./B

如果必须描述此命令,我会说这是一个从生产者(A)读取(即读取)并写入消费者(B)的命令。这是正确的描述吗?我有什么想念的吗?



它不是命令,而是由bash进程创建的kenerl对象,它用作进程A的stdout,用作b的stdin。两个进程几乎同时启动。
炸鱼薯条德里克

1
@炸鱼你是正确的-内核管道是在文件系统pipefs的对象,但据壳本身而言-在技术上这是一个管道命令
谢尔盖Kolodyazhnyy

Answers:


26

关于您的问题,唯一突出的错误是您说

A将首先运行,然后B获得A的标准输出

实际上,这两个程序几乎同时启动。如果B在尝试读取时没有输入,它将阻塞,直到有输入要读取为止。同样,如果没有人从中读取输出A,则其写入将被阻塞,直到读取其输出为止(其中一些将由管道缓冲)。

同步参与管道的进程的唯一方法是I / O,即跨管道的读取和写入。如果没有书写或阅读,则这两个过程将完全彼此独立地运行。如果一个忽略另一个进程的读取或写入,则另一个进程终止时,被忽略的进程将阻塞并最终被SIGPIPE信号杀死(如果正在写入),或者在其标准输入流上出现文件结束条件(如果读取) 。

惯用的描述方式A | B是它是一个包含两个程序的管道。在第一个程序的标准输出上产生的输出可被第二个在标准输入上读取(“ [的输出] A通过管道传递到[的输入] B”)。外壳会执行所需的管道以允许这种情况发生。

如果您想使用“消费者”和“生产者”一词,我想也可以。

这些是用C编写的程序,这一事实无关紧要。这与Linux,macOS,OpenBSD或AIX无关。


2
在DOS中使用了写入临时文件的方式,因为它不支持多个进程。
CSM

2
@AlexVong请注意,带有临时文件的示例并不完全等效。程序可能会选择查找文件的内容,但是管道中的数据是不可搜索的。一个更好的例子是用来mkfifo创建一个命名管道,然后在后台从管道中读取B,然后在A中写入。不过这是挑剔的,因为效果是一样的。
Kusalananda

2
@AlexVong在该文章中进行的简化使其与实际管道分离;并行执行实际上是语义上的,而不是优化。对于看过shell管道的人来说,这是对单子评估或组成的合理的对孩子的谎言,但在另一个方向上是无效的。Kusalananda的fifo版本更接近,但是模型的错误传播部分确实很重要,并且不可复制。(作为一个非常讲究“ shell管道只是功能组合”的人,我都说过这些)
Michael Homer

6
@AlexVong不,那完全偏离了轨道。连简单的事情都无法解释 yes | sed 10q
比利叔叔

1
@UncleBilly我同意你的例子。这表明Michael确实也确实需要并行执行。否则,我们将无法终止。
Alex Vong

2

文档中通常使用的术语是“管道”,由一个或多个命令组成, 请参阅POSIX定义。 从技术上来说,这是您拥有的两个命令,两个用于shell的子进程(fork()+exec()ed外部命令或subshel​​l)

至于生产者-消费者部分,可以用该模式描述管道,因为:

  • 生产者和消费者共享固定大小的缓冲区,至少在Linux和MacOS X上, 管道缓冲区固定大小
  • 生产者和消费者之间是松散耦合的,管道中的命令不知道彼此的存在(除非他们正在积极检查/proc/<pid>/fd目录)。
  • 生产者写信,stdout而消费者则读stdin它们,就好像它们是一个正在执行的命令一样,也可以彼此独立存在

我在这里看到的区别是,与其他语言中的Producer-Consumer不同,shell命令使用缓冲,并且一旦缓冲区被填充,它们便会写stdout,但是没有提及Producer-Consumer必须遵循该规则-仅在队列被填充或丢弃时等待数据(管道不执行的其他操作)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.