当输出文件为 /dev/null 时,为什么 tar 似乎跳过文件内容?

Rus*_*lan 22 tar null

我有一个包含超过 400 GiB 数据的目录。我想检查所有文件是否可以正确读取,所以我想到的一个简单方法是将tar其放入/dev/null. 但相反,我看到了以下行为:

$ time tar cf /dev/null .

real    0m4.387s
user    0m3.462s
sys     0m0.185s
$ time tar cf - . > /dev/null

real    0m3.130s
user    0m3.091s
sys     0m0.035s
$ time tar cf - . | cat > /dev/null
^C

real    10m32.985s
user    0m1.942s
sys     0m33.764s
Run Code Online (Sandbox Code Playgroud)

上面的第三个命令在运行了很长时间后被Ctrl+强行停止C。此外,当前两个命令正在工作时,包含的存储设备的活动指示器.几乎总是空闲的。使用第三个命令时,指示灯会持续亮起,表示非常忙碌。

所以看起来,当tar能够发现它的输出文件是 时/dev/null,即当/dev/null直接打开有tar写入的文件句柄时,文件体似乎被跳过了。(添加v选项tar确实将目录中的所有文件打印为tar“红色”。)

所以我想知道,为什么会这样?这是某种优化吗?如果是,那么为什么还要tar对这种特殊情况进行如此可疑的优化?

我在 Linux 4.14.105 amd64 上使用 GNU tar 1.26 和 glibc 2.27。

mur*_*uru 25

一个记录在案的优化

当存档被创建到 时/dev/null,GNU tar 会尽量减少输入和输出操作。Amanda 备份系统与 GNU tar 一起使用时,具有使用此功能的初始大小调整通道。

  • 这是一个[公认的问题](https://www.xkcd.com/912/)。 (18认同)
  • 他们真的应该保持 man 和 info 页面同步,这实际上是一个错误,他们没有 (9认同)
  • @Ruslan 对于大多数 GNU 实用程序,手册页仅包含一个简短的摘要,基本上只有当您记住它有一个选项可以执行某些操作但不记得该选项的名称时才足够好。完整文档的格式不能很好地转换为手册页,并且可以通过 `info` 或在浏览器中作为 HTML 使用。 (9认同)
  • 啊,这在我安装的手册页中没有描述。应该尝试使用`info tar`代替...... (4认同)
  • 它有记录,但没有得到很好的解释,我觉得像这样令人惊讶和非 Unixy 的行为值得一些理由。目前尚不清楚为什么 Amanda 不能简单地不调用 `tar`,或者如果必须的话,使用一个特殊的 --do-not-write 标志来实现这一点,而不是(IMO)在 30 年后继续破坏 `tar` (3认同)

Gun*_*ohm 8

这可能发生在各种程序中,例如,我在使用cp file /dev/null;时曾经有过这种行为;该命令在几毫秒后返回,而不是估计我的磁盘读取速度。

据我所知,那是在 Solaris 或 AIX 上,但该原理适用于各种 unix-y 系统。

在过去,当程序将文件复制到某个地方时,它会在read从磁盘(或文件描述符所指的任何内容)获取一些数据到内存(保证read返回时一切都在那里)的write调用和调用之间交替(获取内存块并将内容发送到目的地)。

但是,至少有两种较新的方法可以实现相同的目标:

  • Linux 有系统调用copy_file_range(根本不能移植到其他 unix)和sendfile(有点可移植;最初打算将文件发送到网络,但现在可以使用任何目的地)。它们旨在优化传输;如果程序使用其中之一,很容易想象内核会识别目标/dev/null并将系统调用转换为无操作

  • 程序可以mmap用来获取文件内容而不是read,这基本上意味着“当我尝试访问该内存块时确保数据在那里”而不是“当系统调用返回时确保数据在那里”。因此,程序可以mmap访问源文件,然后调用write该映射内存块。但是,由于写入/dev/null不需要访问写入的数据,因此不会触发“确保它在那里”条件,从而导致文件也未被读取。

不确定 gnu tar 在检测到它正在写入时是否使用这两种机制中的任何一种,以及这两种机制中的哪一种/dev/null,但它们是为什么任何程序在用于检查读取速度时都应该使用| cat > /dev/null而不是> /dev/null- 以及为什么| cat > /dev/null应该运行的原因在所有其他情况下都应避免