例如使用管道时
sudo cat /dev/sda | strings | less
我可以在 sda 设备的字符串中移动。但是sda设备的内容是否完全加载并输出到cat的输出流中呢?或者每当程序请求 cat 的输出时都会评估新行? (即我在 less 寻呼机上按 j)
答案1
这更多地与如何工作有关less
,而不是与如何cat
或strings
工作有关。
该cat
命令只会将数据推送到其标准输出,并且只要其之间的管道缓冲区strings
已满并且没有人在读取,它就会阻塞。 cat
本身进行最小缓冲,并且管道缓冲区通常很小。
对于 来说也是如此strings
。它将处理来自的数据并在不读取产生的数据cat
时阻塞。less
strings
less
将缓冲其输入,以便您可以在其显示的数据中来回移动。当您滚动到下一页时,将从其缓冲区中less
读取更多数据。strings
当您不向前滚动时,我相信less
只会读取有限数量的数据(因此当您不向前滚动时将被阻止)strings
。cat
如果将大量数据通过管道传输到less
,则将使用大量内存进行缓冲如果您决定将其全部读完less
。
有一个选项 ,-B
将用于缓冲的内存量限制为 64 KB(或您使用该-b
选项指定的大小)。以这种方式限制缓冲区大小将防止您回滚超过指定缓冲区空间中可以存储的内容,但也允许您读取大量数据而不会less
耗尽内存。
另请参阅man less
您的系统。
答案2
管道的缓冲区空间有限,如果管道读取器(例如less
在您的示例中)没有从管道读取更多数据,则写入器在填充缓冲区后将被阻塞。这将影响该strings
命令,而该命令又会在cat
其管道已满后阻止该命令。
当然,该cat
命令无法将整个 sda 设备内容读取到主内存中,因此如果尚未读取的块正在更改,cat
则会看到更改的内容。
答案3
和cat
以及strings
大多数类似的实用程序1,一次读取一点输入,对其进行处理,然后读取更多输入,依此类推。因此,就您而言,cat
仅读取less
显示的内容,再加上一些正在传输的内容。
更详细地说,基本操作cat
是:
- 保留几千字节的内存用作缓冲区。
- 虽然还有更多可用的输入:
- 将最多 N 个字节的输入读取到缓冲区中。 (这会覆盖上一个周期中写出的数据。)
- 将缓冲区内容写入输出。
写操作会阻塞,直到有地方可以复制输出。当一个管道输出时,管道本身会消耗内核中的一点内存,这称为管道缓冲区。一旦满了,如果cat
尝试写入管道,写入尝试将被阻止,直到有空间为止。当管道读取端的进程读取一些数据时,管道缓冲区中可能有空间。
该strings
程序的工作方式与 相同cat
,只是它不复制整个输入,仅复制选定的部分。
该less
程序的工作方式有点不同:它将读取的所有内容保留在内存中。它不会回收其缓冲区,只要有更多输入不断进入,它就会不断增长缓冲区。但是,读取部分是相似的,因为less
仅在需要时才读取数据:它只读取到显示的最后一行,再加上预期读取的内容(如果有)。
因此,当您运行时sudo cat /dev/sda | strings | less
,读取的内容/dev/sda
包括:
less
已显示(或滚动过去)的数据。less
已读取但尚未显示的数据最多为几 kB 。strings
和之间的管道缓冲区最多为几 kBless
。- 内存中最多可达几 kB
strings
。 cat
和之间的管道缓冲区最多为几 kBstrings
。- 内存中最多可达几 kB
cat
。
您可以通过跟踪其系统调用来观察每个程序何时读取和写入数据:
sudo strace -e read,write -o cat.strace cat /dev/sda | strace -e read,write -o cat.strace strings | strace -e read,write -o less.strace less
并观看*.strace
文件。您还可以cat
通过检查文件偏移量来检查已读取的数据量,例如使用lsof -p1234
或使用head /proc/1234/fdinfo/0
where 1234
is 的进程 ID cat
。
1在基本文本处理实用程序中,主要的例外是sort
,它在读取整个输入之前无法发出任何输出:据它所知,输出的第一行很可能是它到达的输入的最后一行。
答案4
在某些系统(例如 MS-Dos)上,管道是通过将第一个命令的输出复制到文件,然后运行第二个命令从该文件读取来实现的。 Unix 不这样做。
在 Unix 上它就像一条生产线。每个阶段同时工作,读取输入并产生输出。如果流程 A 的生产速度快于流程 B 的消耗速度,则流程 A 和 B 之间会积累库存。当库存过多时(½KiB 到 4 KiB),流程 A 将暂停。当没有库存可供 B 处理时,B 就会暂停。流程会暂停和取消暂停,以保持较低的库存水平。
这些程序中的代码不关心这些。它只是读取输入并写入输出。如果它在数据可用之前尝试读取,或者在下一个进程准备好之前尝试写入,则操作系统将暂停它,直到准备好。
当没有更多内容可供读取时(并且没有更多内容正在读取),读取器将收到文件结尾并将退出。这反过来会在下一个进程中触发文件结束。