我只是在终端中运行一些命令,然后开始思考,在运行管道命令时,Unix / Linux是否采用快捷方式?
例如,假设我有一个包含一百万行的文件,其中前十行包含hello world
。如果运行该命令grep "hello world" file | head
,第一个命令找到10行后会立即停止,还是先继续搜索整个文件?
我只是在终端中运行一些命令,然后开始思考,在运行管道命令时,Unix / Linux是否采用快捷方式?
例如,假设我有一个包含一百万行的文件,其中前十行包含hello world
。如果运行该命令grep "hello world" file | head
,第一个命令找到10行后会立即停止,还是先继续搜索整个文件?
Answers:
有点。Shell不知道您正在运行的命令将执行什么操作,它只是将一个命令的输出连接到另一个命令的输入。
如果grep
发现多于10条说“ hello world”的行,head
则将拥有它想要的所有10条线,然后关闭管道。这将导致grep
被SIGPIPE杀死,因此不需要继续扫描非常大的文件。
grep
会继续将输出发送到/dev/null
当程序尝试写入管道并且没有从该管道读取进程时,写入器程序将收到SIGPIPE信号。程序收到SIGPIPE时的默认操作是终止程序。程序可以选择忽略SIGPIPE信号,在这种情况下,写操作会返回错误(EPIPE
)。
在您的示例中,这是发生情况的时间表:
grep
和head
平行的命令启动。grep
读取一些输入,开始处理它。grep
产生第一块输出。head
读取第一个块并将其写出。grep
可能会先终止),最终head
将打印出所需的行数。此时,head
退出。grep
和head
处理的相对速度,grep
可能已经积累了一些数据而尚未打印出来。在head
退出时,grep
可能正在读取输入或进行内部处理,在这种情况下,它将继续这样做。grep
就会写出处理过的数据。届时,它将收到SIGPIPE并死亡。很可能grep
会处理多一点投入比严格意义上,但通常只有几KB:
head
通常读取几千字节的块(因为这比read
为每个字节发出系统调用更有效-这种行为称为缓冲),因此丢弃了所需最后一行之后的最后一块剩余部分。grep
可能已经积累了一些准备好成为输出块的数据(再次缓冲)。尝试刷新其输出缓冲区时,它将收到SIGPIPE。总体而言,系统经过精心设计,因此过滤实用程序自然可以高效地运行。当输出通道消失时需要继续运行的程序必须采取忽略SIGPIPE信号的步骤。
排序,管道的工作方式如下:在您的情况下,它首先执行第一个命令,然后执行第二个命令。
也就是说,让我们A|B
给出命令。然后不确定是先开始A
还是B
先开始。如果有多个CPU,则它们可能完全同时启动。管道可以容纳未定义但数量有限的数据。
如果B试图从管道读取,但没有可用数据,B
则将等待数据到达。如果B
正在从磁盘读取,则B
可能有相同的问题,需要等到磁盘读取完成。更加接近的类比是从键盘读取。在那里,B
需要等待用户键入。但是在所有这些情况下,B已经开始“读取”操作,必须等待其完成。但是,如果B
某条命令仅需要部分输出,A
则在B
达到s输入级别的某个点之后A
,SIGPIPE将终止该命令
如果A
试图写入管道并且管道已满,则A
必须等待管道中的一些空间可用。A
如果正在写入终端,则可能会有相同的问题。终端具有流量控制功能,可以调节数据速度。无论如何,对于A
,它已经开始了“写”操作,并将等待直到写操作完成。
A
并且B
表现为协同流程,尽管并非所有协同流程都将与管道进行通信。双方都无法完全控制对方。
head
出口),则会在程序中出现SIGPIPE信号,并且默认行为是退出。
-m
参数。