输出非常大的管道命令

Liv*_*ivy 20 pipe shell-script

我想要tar一个目录并将结果写入stdout,然后将其通过管道传输到压缩程序,如下所示:

tar -cvf - /tmp/source-dir | lzip -o /media/my-usb/result.lz -
Run Code Online (Sandbox Code Playgroud)

我一直在使用管道来输出多行文本的命令。现在我想知道当我用管道输出一个(快速)命令时会发生什么,例如非常大的输出,tar然后是一个非常慢的压缩命令?会tar等待它的输出被消耗掉lzip吗?或者它只是尽可能快地将所有内容输出到RAM?如果后者属实,那么在低 RAM 系统中将是一场灾难。

Kus*_*nda 45

当数据生产者 ( tar) 试图太快地写入管道而消费者 ( lzip) 没有时间读取所有数据时,它将阻塞,直到lzip有时间读取tar正在写入的内容。有一个与管道相关联的小缓冲区,但其大小可能小于大多数tar档案的大小。不存在管道填满系统 RAM 的风险。

“阻塞”只是意味着当tar调用write()库函数(或等效函数)时,调用不会返回,直到数据被传递到管道缓冲区,如果lzip从中读取速度很慢,这可能需要一些时间相同的缓冲区。与相比(假设实际上比 更快)相比,您应该能够toptar会放慢速度和睡眠的地方看到这一点。lziptarlzip

因此,您的管道不会填满大量 RAM。要做到这一点(如果你愿意的话),你可以pv在中间使用类似的东西,有一些大的缓冲区(这里是一个千兆字节):

tar -cvf - /tmp/source-dir | pv --buffer-size 1G | lzip -o /media/my-usb/result.lz -
Run Code Online (Sandbox Code Playgroud)

tar每当阻塞时,这仍然会pv阻塞。 pv当其缓冲区已满且无法写入时会阻塞lzip


相反的情况以类似的方式工作,即如果管道的左侧缓慢写入到快速右侧,则右侧的使用者将阻塞,read()直到有数据要从管道读取。

这(数据 I/O)是同步参与管道的进程的唯一内容。除了读取和写入(偶尔会在等待其他人读取或写入时阻塞),它们会彼此独立运行。

  • @LieRyan我不知道`pv`,但是我想到的大缓冲区的用例是从计时连接或从你想尽快移除的USB记忆棒中读取,或者通常从任何进程中读取锁定关键资源。 (4认同)
  • FWIW,管道缓冲区的大小取决于操作系统和版本。真正的老式 Linux 的缓冲区大小约为 4Kb。后来它默认为 64Kb 之类的东西,并提供了一个 `fcntl()`,可以根据需要调整开放管道缓冲区的大小(例如到 1M)。 (2认同)
  • 来自 https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer "PIPE_BUF" 是可以_原子地_写入的最大值。`sysctl fs.pipe-user-pages-soft` 在我的机器上返回 16384,而 `sysctl fs.pipe-max-size` 返回 1048576 (2认同)
  • 从网络管道到 I/O 绑定的进程,该进程以块的形式写入是另一个派上用场的地方。几项工作之前(所以细节模糊),我有一项工作可以有效地定期刷新磁盘,并在它们进行时挂起很长一段时间;没有 `pv` 和一个大缓冲区,这会停止下载,这意味着吞吐量的重大损失。当然,如果您的 I/O 系统以合理稳定的速度刷新,则没有太大区别;但情况并非总是如此。 (2认同)