考虑一个 100MB 的原始块设备作为一个简单的例子。即 204800 个块,每个块 512 字节,总共 102760448 字节。
挑战在于移动前 98MB(200704 个块),因此在它前面有 2MB(4096 个块)的间隙。就地执行此操作要求未向尚未读取的扇区写入任何内容。实现此目的的一种方法是引入缓冲区:
$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 | dd of=/dev/sdj2 seek=4096
Run Code Online (Sandbox Code Playgroud)
期望是mbuffer
在将任何内容传递给写入器之前将存储 4096 个块,从而确保没有任何内容写入尚未读取的区域,并且写入器滞后读取器缓冲区的大小。缓冲区应该允许读取器和写入器在这些约束内尽可能快地操作。
但是,它似乎不能可靠地工作。我试过使用真实的设备,但它从来没有在它们上工作过,而对文件的实验在我的 64 位机器上有效,但在我的 32 位机器上无效。
首先,一些准备工作:
$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile
Run Code Online (Sandbox Code Playgroud)
这不起作用:
$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=/dev/sdj2 seek=4096
summary: 98.0 MiByte in 4.4sec - average of 22.0 MiB/s
md5 hash: 3cbf1ca59a250d19573285458e320ade
Run Code Online (Sandbox Code Playgroud)
这适用于 64 位系统,但不适用于 32 位系统:
$ dd if=testfile count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=testfile seek=4096 conv=notrunc
summary: 98.0 MiByte in 0.9sec - average of 111 MiB/s
md5 hash: 0f0727f6644dac7a6ec60ea98ffc6da9
Run Code Online (Sandbox Code Playgroud)
如何可靠地做到这一点?
笔记
我已经阅读了有关缓冲的其他问题,并查看了pv
,buffer
和mbuffer
. 我只能让后者使用所需的缓冲区大小。
使用中间存储是解决始终有效但在没有足够备用容量可用时不切实际的问题的明显解决方案。
运行 Arch Linuxmbuffer
版本 20140302 的测试平台。
如果没有缓冲区,您可能会一次向后退一个块。
for i in $(seq 100 -1 0)
do
dd if=/dev/thing of=/dev/thing \
bs=1M skip=$i seek=$(($i+2)) count=1
done
Run Code Online (Sandbox Code Playgroud)
请注意,由于缺乏错误检查,此示例很危险。
由于dd
呼叫量大,速度也很慢。如果您有空闲内存,则可以使用更大的块大小。
有了缓冲区,就要小心陷阱。仅仅保证 100% 预填充是不够的。您需要的是整个过程中的最小填充量。缓冲区绝不能低于以下2M
值,否则您将再次覆盖尚未读取的数据。
因此,理论上您可以不使用任何类型的缓冲区而只使用链dd
:
dd if=/dev/thing bs=1M | \
dd bs=1M iflag=fullblock | \
dd bs=1M iflag=fullblock | \
dd of=/dev/thing bs=1M seek=2
Run Code Online (Sandbox Code Playgroud)
实际上,这并不可靠,因为不能保证第一个dd
设法继续读取数据,而最后一个dd
(2M
中间有“缓冲区”)已经在写入。
您可以通过使中间缓冲区变得相当大来大大增加机会,但即便如此,它仍然不可靠。
不幸的是,我不知道具有最小填充属性的良好缓冲程序。只要缓冲区内的安全裕度小于安全裕度,您就需要一种能够停止输出的设备。