为什么从gunzip到dd管道最后会变慢?

为什么从gunzip到dd管道最后会变慢?

我的命令:

gunzip -c serial2udp.image.gz |
sudo dd of=/dev/mmcblk0 conv=fsync,notrunc status=progress bs=4M

我的输出:

15930949632 bytes (16 GB, 15 GiB) copied, 1049 s, 15.2 MB/s    

0+331128 records in
0+331128 records out
15931539456 bytes (16 GB, 15 GiB) copied, 1995.2 s, 8.0 MB/s

卡:SanDisk Ultra 32GB MicroSDHC Class 10 UHS 存储卡速度高达 30MB/s
发行版:16.0.4 xenial,带 xfce
内核版本:4.13.0.37-generic

根据我所读到的内容,我知道花费 17 分钟似乎是合理的。调整块大小似乎并没有多大区别(bs=100M 仍然表现出具有类似时间戳的这种行为)。为什么更新挂起并且在另外 16 分钟内没有生成完成的报告?

iotop 告诉我此时 mmcqd/0 仍在后台运行(99% IO),所以我认为某个地方有一个缓存保留了最后的 5MB,但我认为 fsync 应该确保这种情况不会发生iotop 显示此时 dd 也没有交通交叉口。 ctrl-c 几乎没用,我不想在写入后损坏我的驱动器。

答案1

我认为某个地方有一个缓存保留了最后的 5MB,但我认为 fsync 应该确保这种情况不会发生

conv=fsync表示在写入所有数据fsync后通过调用 -写回所有缓存。dd挂在最后正是它要做的事情。

当输出文件比输入文件慢时,写入的数据dd可能会堆积在缓存中。内核缓存有时会占满系统 RAM 的很大一部分。这会产生非常具有误导性的进度信息。您的“最后 5MB”只是dd显示进度的人工制品。

如果您的系统确实缓存了大约 8GB(即 16GB 写入数据的一半),那么我认为您要么必须拥有大约 32GB 的 RAM,要么一直在摆弄某些内核选项。请参阅下面的 lwn.net 链接。我同意,15 分钟内没有收到任何进度信息是相当令人沮丧的。

您可以使用其他dd命令。如果您想dd显示更准确的进度,您可能必须接受更多的复杂性。我希望以下内容不会降低您的表现,尽管现实可能有与我不同的想法。

gunzip -c serial2udp.image.gz |
dd iflag=fullblock bs=4M |
sudo dd iflag=fullblock oflag=direct conv=fsync status=progress bs=4M of=/dev/mmcblk0
  • oflag=direct iflag=fullblock避免堆积内核缓存,因为它完全绕过它。
  • iflag=fullblockAFAIK 在这样的命令中是必需的(例如,因为您正在从管道读取并使用直接 IO 写入)。缺失的影响fullblock是另一个不幸的复杂性dd。该网站上的一些帖子使用这一点来论证您应该始终更喜欢使用不同的命令。但很难找到另一种方法来进行直接或同步 IO。
  • conv=fsync仍然应该使用,写回设备缓存。
  • 我添加了一个额外的ddafter gunzip,以与磁盘写入并行缓冲解压缩的输出。这是使性能变得复杂oflag=directoflag=sync有点复杂的问题之一。普通 IO(非直接、非同步)不需要这个,因为它已经被内核缓存缓冲了。如果您要写入具有 4M 回写缓存的硬盘,您可能也不需要额外的缓冲区,但我不认为 SD 卡有那么多。

您也可以使用oflag=direct,sync(并且不需要conv=fsync)。如果你有一个奇怪的输出设备,这可能对良好的进度信息很有用数百兆字节的缓存。但通常我认为oflag=sync这是性能的潜在障碍。

有一篇2013年的文章https://lwn.net/Articles/572911/其中提到了像您这样的长达一分钟的延迟。许多人认为这种缓存几分钟写回数据的能力是不可取的。问题在于,对高速缓存大小的限制不加区别地应用于快速和慢速设备。请注意,内核测量设备速度并非易事,因为它会根据数据位置而变化。例如,如果缓存的写入分散在随机位置,则硬盘驱动器将需要更长的时间来重复移动写入磁头。

为什么更新挂起

fsync()是一个适用于整个范围的单个系统调用文件设备。在完成之前它不会返回任何状态更新。

答案2

你想用fullblock.notrunc对设备文件没有影响。

dd of=/dev/mmcblk0 iflag=fullblock status=progress bs=4M

答案3

本身不是一个答案,而是更多的诊断。使用管道视图实用程序pv查看各个管道流量的变化情况,例如:

pv -c -N raw serial2udp.image.gz |
gunzip |
pv -c -N uncompressed |
sudo dd of=/dev/mmcblk0 conv=fsync,notrunc status=progress bs=4M

假设:

  1. 如果最后速度变慢,则可能是.gz文件在最后压缩得最严重,或者需要做gunzip更多工作。
  2. 另一种可能性是写入介质本身在某些部分速度较慢,这可能是由于设计的特殊性甚至是坏/边缘块。可以通过以下方式完成非破坏性读/写测试:badblocks -nv /dev/mmcblk0

相关内容