Linux中的管道是如何工作的?

42

管道是如何工作的?如果我通过命令行界面运行程序并将输出重定向到文件,那么在该文件被写入时,我是否能够将该文件作为输入流传递给另一个程序?

基本上,当一行内容被写入文件时,我希望它能立即被传递到第二个应用程序中(我正在尝试从现有程序动态绘制图形)。只是不确定管道是否会在移动到下一个命令之前完成第一个命令。

任何反馈都将不胜感激!


你在下面提到了program1的CLI接口——如果program1依赖于交互式输入,那么它很可能不适合像Adam的答案所示的直接管道处理。 - Harper Shelby
在Windows中,管道与Linux中的不同。我认为Linux使用某种类型的环形缓冲区,并使用并发编程理论(生产者-消费者问题等)来实现良好的性能。我认为Windows会等待第一个程序完成后才调用第二个程序。 - Ape-inago
4个回答

66

如果你想将一个程序的输出重定向为另一个程序的输入,只需要使用简单的管道:

program1 arg arg | program2 arg arg

如果你想将program1的输出保存到一个文件中,同时又要将它传递给program2,你可以使用tee(1)命令:

program1 arg arg | tee output-file | program2 arg arg

管道中的所有程序同时运行。大多数程序通常使用阻塞I/O:如果它们在尝试读取输入时没有任何内容可用,它们会阻塞:也就是说,它们停止运行,并且操作系统将其取消调度以等待更多输入(以避免占用CPU)。同样地,如果管道中较早的程序写入数据的速度比后续程序读取数据的速度快,最终管道的缓冲区将填满并导致写入程序阻塞:操作系统将其取消调度直到读取程序清空了管道的缓冲区,然后它可以继续写入。


编辑

如果您想使用program1的输出作为命令行参数,您可以使用反引号或$()语法:

# Runs "program1 arg", and uses the output as the command-line arguments for
# program2
program2 `program1 arg`

# Same as above
program2 $(program1 arg)

应该优先使用$()语法,因为它们更清晰,并且可以嵌套。


由于某些原因,初始程序(在本例中为program1)不与管道配合使用。它需要特殊的代码吗?我尝试按照您提供的简单管道操作,但是1)program1的CLI界面没有启动,2)program2要求输入参数(尽管我正在尝试将1的输出作为2的参数传递)。 - Jon
@Jon,听起来像是program1没有将其输出发送到stdout或者program2没有从stdin读取。你正在尝试运行哪些程序? - Matthew Crumley
有一个标志,一些程序可以读取它来判断它们是否在管道中... 'ls' 就是这样做的,以决定如何显示文件信息(当在管道中时,每个文件都有自己的一行等)。 - Ape-inago
@Adam:我上面写的测试管道只是为了更好地学习Linux。原始程序有太多基于Linux的库,无法在Windows上进行任何操作。原始程序本身是一个测试程序,它接受一系列输入以确定适当的输出(随机和动态更改)。输出通过网络传输(或在我的情况下传输到另一个终端),然后打印出来。我的任务是编写一个程序,将该输入变得“漂亮”,例如图形。这至少是基本想法。 - Jon
顺便提一下,如果你想要一个读取标准输入并将其简单地重复到标准输出的程序,请使用“cat”,而不是“echo”。 - Lie Ryan
显示剩余2条评论

34

管道在运行第二个命令之前不会完成第一个命令。Unix(和Linux)中的管道同时运行所有命令。如果一个命令:

  • 需要输入但是没有得到。

  • 已经产生的输出比其后继者准备消耗的要多。

对于大多数程序,输出是缓冲的,这意味着操作系统在将输出传递给管道的下一个阶段之前累积了大量输出(可能是大约8000个字符)。这种缓冲是为了避免进程和内核之间过多的切换。

如果您希望管道上的输出能够立即发送,您可以使用非缓冲的I/O,这在C语言中意味着调用类似于fflush()这样的东西来确保任何缓冲输出都立即被发送到下一个进程中。非缓冲输入也是可能的,但通常是不必要的,因为饥饿等待输入的进程通常不会等待完整的缓冲区,而是会处理您可以获得的任何输入。

对于典型的应用程序,不建议使用非缓冲输出;通常情况下默认设置可以获得最佳性能。但是,在您需要在第一个进程有信息可用时立即进行动态图形处理的情况下,您绝对需要使用非缓冲输出。如果您正在使用C语言,只需在需要发送输出时调用fflush(stdout)即可。


10

5

如果您的两个程序坚持要读写文件而不使用stdin/stdout,您可能会发现可以使用命名管道代替文件。

使用mknod(1)命令创建一个命名管道:

$ mknod /tmp/named-pipe p

然后配置你的程序以读写/tmp/named-pipe(使用你认为合适的任何路径/名称)。

在这种情况下,两个程序将并行运行,在管道变满/空时按需要阻塞,如其他答案所述。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接