我从来没有真正考虑过 shell 是如何实际执行管道命令的。我总是被告知“一个程序的标准输出管道式的进入另一个的标准输入”,作为考虑管道的一种方式。所以很自然地,我认为在这种情况下,A | B
,A
会首先运行,然后B
获取 的标准输出A
,并使用 的标准输出A
作为其输入。
但我注意到,当人们在 中搜索特定进程时ps
,他们会grep -v "grep"
在命令末尾包含该进程,以确保该进程grep
不会出现在最终输出中。
这意味着在命令中ps aux | grep "bash" | grep -v "grep"
暗示知道ps
该命令grep
正在运行,因此位于 的输出中ps
。但是,如果ps
在其输出通过管道传输到 之前完成运行grep
,那么它如何知道grep
正在运行?
flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*"
PID TTY TIME CMD
3773 pts/0 00:00:00 bash
3784 pts/0 00:00:00 ps
3785 pts/0 00:00:00 grep
答案1
管道命令同时运行。当您运行时,是否先启动或先启动ps | grep …
取决于运气(或者是 shell 工作细节与内核内部调度程序微调相结合的问题),并且在任何情况下它们都会继续启动。同时执行。ps
grep
这非常常用来允许第二个程序在第一个程序完成其操作之前处理来自第一个程序的数据。例如
grep pattern very-large-file | tr a-z A-Z
grep
甚至在完成遍历大文件之前就开始以大写形式显示匹配行。
grep pattern very-large-file | head -n 1
grep
显示第一个匹配行,并且可能在完成读取其输入文件之前停止处理。
如果您在某处读到管道程序按顺序运行,请逃离此文档。管道程序总是同时运行。
答案2
命令运行的顺序实际上并不重要,并且不能保证。抛开pipe()
、fork()
、dup()
和的神秘细节不谈execve()
,shell 首先创建管道,即数据在进程之间流动的管道,然后创建进程,管道的两端连接到进程。运行的第一个进程可能会阻塞等待来自第二个进程的输入,或者阻塞等待第二个进程开始从管道读取数据。这些等待可以是任意长的,但并不重要。无论进程以哪种顺序运行,数据最终都会被传输并且一切正常。
答案3
冒着死马的风险,误解似乎是
A|乙
相当于
A>临时文件 乙<临时文件 R M临时文件
但是,当 Unix 创建时,孩子们骑着恐龙去上学,磁盘非常小,一个相当良性的命令通常会消耗文件系统中的所有可用空间。如果B
是这样的话 ,管道的最终输出可能是grep some_very_obscure_string
很多小于该中间文件。因此,管道的开发,并不是作为“跑”的简写。A首先,然后运行乙输入来自A的输出”模型,但作为B
并行执行A
并消除在磁盘上存储中间文件的需要的一种方式。
答案4
你问到了订单的问题,我认为这是事情的一个非常重要的方面。这不是随机的(正如吉尔斯在他的回答中试图说的那样)。
这是ps -ef
通过管道传送到的grep
命令:
$ ps -ef | grep .
...
alexis 37188 55443 0 20:17 pts/4 00:00:00 ps -ef
alexis 37189 55443 0 20:17 pts/4 00:00:00 grep --color=auto .
...
注意:我从输出中删除了所有其他进程,因为它们对此事并不重要。
正如我们所看到的,输出中有 aps -ef
和 a 。grep --color=auto .
你现在可以回答你的问题了吗?
是的。该ps
命令的 PID 为 37,188,该grep
命令的 PID 为 37,189。显然,它们是从左到右创建的,任何 shell 都不应该以不同的方式执行此操作。
从技术上讲,在 C 中,我们使用以下命令创建管道pipe(2)
函数给我们两个文件描述符。一个将用作stdout
of ps
,另一个将用作stdin
of grep
。stdin
在开始之前保留文件描述符是很容易的ps
。
此外,如果您像这样查看系统配置:
$ getconf -a | grep PIPE_BUF
PIPE_BUF 4096
_POSIX_PIPE_BUF 4096
您注意到这两个参数定义了管道的最小保证大小(以字节为单位)。从 Linux 2.6 开始,默认大小为 64Kb。此外,绝对最大字节数定义如下:
$ cat /proc/sys/fs/pipe-max-size
1048576
我们可以看到这是 1Mb。一旦管道已满,输出器(ps
在我们的第一个示例中)就会阻塞,直到管道另一侧的进程读取数据(grep
在我们的第一个示例中)。
换句话说,由于 的输出ps
远小于管道的大小:
$ ps -ef | wc
1132 10819 121435
(即目前我的计算机上大约有 120Kb 的输出......)
管道根本不会被堵塞。
对于超过 1Mb 的流数据,它确实会在某些时候被阻塞。如果grep
不立即启动,它将永远不会启动,因为write()
第一个命令中的调用将被阻止。
因此,这些进程很快就会连续启动,但在大多数情况下,它们是并行运行的(或者如果您有单个处理器,则同时运行)。也就是说,ps
命令会先死掉。这将管道标记为“完成”(EOF
从其中读取数据时会收到信号),这就是下一个工具知道它已完成的方式,并且一旦处理完接收到的最后几个字节,它也会死亡。
相反,如果管道右侧的进程提前终止(在左侧进程完成对管道的写入之前),则左侧的进程SIGPIPE
一旦尝试写入管道就会收到信号。这样做是为了确保如果管道中的任何进程终止,管道也会快速终止。