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

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

我有一个目录,其中包含超过 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

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

如此看来,当tar能够查出其输出文件为 时/dev/null,即/dev/null直接打开有tar写入的文件句柄时,文件体出现跳过。 (添加v选项确实tar会打印目录中的所有文件为tar“红色”。)

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

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

答案1

记录的优化:

当归档文件被创建到 时/dev/null,GNU tar 会尝试最小化输入和输出操作。 Amanda 备份系统与 GNU tar 一起使用时,有一个使用此功能的初始大小调整过程。

答案2

这种情况可能发生在各种程序中,例如,我在使用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应该是避免的在所有其他情况下。

相关内容