我刚刚在终端中运行了几个命令,我开始想知道,Unix/Linux 在运行管道命令时是否采用快捷方式?
例如,假设我有一个包含 100 万行的文件,其中前 10 行包含hello world
.如果运行该命令,grep "hello world" file | head
第一个命令是否会在找到 10 行后立即停止,还是会先继续搜索整个文件?
答案1
有点。 shell 不知道您正在运行的命令将执行什么操作,它只是将一个命令的输出连接到另一个命令的输入。
如果grep
找到超过 10 行“hello world”,head
则将获得所需的所有 10 行,并关闭管道。这将导致grep
被 SIGPIPE 杀死,因此不需要继续扫描非常大的文件。
答案2
当程序尝试写入管道并且没有进程从该管道读取时,写入程序会收到信号管道信号。当程序收到 SIGPIPE 时,默认操作是终止该程序。程序可以选择忽略 SIGPIPE 信号,在这种情况下写入会返回错误 ( EPIPE
)。
在您的示例中,以下是发生情况的时间表:
- 和
grep
命令head
并行启动。 grep
读取一些输入,开始处理它。- 在某个时刻,
grep
产生第一个输出块。 head
读取第一个块并将其写出。- 假设前 10 场比赛后有足够的行(否则
grep
可能会先终止),最终head
将打印出所需的行数。此时,head
退出。 - 根据
grep
和head
进程的相对速度,grep
可能已经积累了一些数据但尚未打印出来。退出时head
,grep
可能正在读取输入或进行内部处理,在这种情况下它将继续这样做。 - 很快
grep
就会写出它处理过的数据。此时,它将收到 SIGPIPE 并死亡。
它可能grep
会处理比严格必要的输入多一点的输入,但通常只有几千字节:
head
通常以几千字节的块读取(因为这比read
为每个字节发出系统调用更有效 - 这种行为称为缓冲),因此在所需的最后一行之后的最后一个块的剩余部分将被丢弃。- 可能有一些数据正在传输,因为管道有一个由内核管理的关联缓冲区(通常为 512 字节)。该数据将被丢弃。
grep
可能已经积累了一些数据,准备成为输出块(再次缓冲)。当它尝试刷新其输出缓冲区时,它将收到 SIGPIPE。
总而言之,该系统经过精确设计,因此过滤实用程序自然可以高效运行。当输出通道消失时需要继续运行的程序必须采取忽略 SIGPIPE 信号的步骤。
答案3
某种程度上,管道的工作原理是这样的:它首先执行第一个命令,然后在您的情况下执行第二个命令。
也就是说,让我们成为A|B
所给出的命令。那么到底是A
orB
先启动就不确定了。如果有多个 CPU,它们可能会同时启动。管道可以容纳未定义但有限数量的数据。
如果 B 尝试从管道中读取数据,但没有可用数据,B
则将等待数据到达。如果B
是从磁盘读取,B
可能会遇到同样的问题,需要等待磁盘读取完成。更接近的类比是从键盘上读取。在那里,B
需要等待用户输入。但在所有这些情况下,B 已开始“读取”操作,并且必须等待其完成。但是 ifB
是一个命令,只需要A
then 的部分输出,在B
达到 s 输入级别的某个点之后A
将被 SIGPIPE 终止
如果A
尝试写入管道并且管道已满,A
则必须等待管道中的一些空间空闲。A
如果写入终端,可能会出现同样的问题。终端具有流量控制功能并且可以调节数据的速度。无论如何A
,它已经启动了“写入”操作,并将等待写入操作完成。
A
并B
表现为协同进程,尽管并非所有协同进程都会与管道进行通信。双方都无法完全控制对方。
答案4
grep
没有对管道的直接控制(它只是接收数据),并且管道没有直接控制grep
(它只是发送数据)...
或任何其他程序做什么grep
,完全取决于该程序的内部逻辑。如果您grep
通过命令行选项告知尽早进行找到后退出,那么它就会,否则它将继续到文件的最后寻找模式......
终端同样与 的内部运作grep
和shell
管道操作完全脱节...终端基本上只是一个发射台和输出显示...