我有一个目录,其中包含超过 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
应该是避免的在所有其他情况下。