通过管道链接 bash 命令时是否发生了任何象征性的事情,或者都是计算-传递-计算-传递?
例如,在 中head t.txt -n 5 | tail -n 2
,正在head t.txt -n 5
计算然后tail -n 2
执行它。或者首先有一些抽象告诉 shell 要读取第 3 行到第 5 行?在这个例子中可能没有什么区别,但我想在其他情况下可以。
答案1
shell 使用pipe(2)
系统调用在内核中创建一个有两个文件描述符的有界缓冲区,一个使进程能够写入缓冲区,另一个使进程能够从缓冲区读取。
考虑一个简单的情况:
$ p1 | p2
在这种情况下,从概念上讲,shell 创建上述管道fork()
s ,子级将其标准输出流连接到管道的写入端,然后是子级exec()
s p1
。接下来,shellfork()
再次 s ,子级将其标准输入流连接到管道的读取端,然后是子级exec()
s p2
。 (我说从概念上来说因为 shell 可能会以不同的顺序执行操作,但想法是相同的。)
那时,p1
和p2
正在同时运行。 p1
将写入管道,内核会将写入的数据复制到缓冲区。 p2
将从管道中读取,内核将从缓冲区中复制读取的数据。如果管道已满,则内核将阻塞p1
其调用,write()
直到p2
从管道中读取某些内容,从而释放一些空间。如果管道为空,则内核将阻塞p2
其调用,read()
直到将p1
更多数据写入管道。
答案2
您建议的两种型号中。计算传递-计算传递是最接近的。 shell 只是连接进程。它对他们在做什么一无所知。
除了, 执行顺序未定义。它们实际上同时运行。然而,左边的必须先输出字节,然后右边的才输入字节。数据从左向右流动。数据从第一个命令流出其标准输出,然后流入下一个进程的标准输入,在那里进行处理,然后从其标准输出出来,可以通过管道传输到另一个进程,等等等等等等
如果没有重定向>
、<
等等或从文件中读取。然后看起来像这样。
┌───────────┐ ┌───────────┐ ┌─────────────┐
Terminal⇨│Process one│⇨│Process two│⇨│Process Three│⇨Terminal
└───────────┘ └───────────┘ └─────────────┘