dd如何用于右移数据块?


10

考虑一个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

期望mbuffer在将任何内容传递给写入器之前将存储4096个块,从而确保未写入任何内容到尚未读取的区域,并且写入器落后于读取器缓冲区大小。缓冲区应允许读取器和写入器在这些内存中尽可能快地操作。

但是,它似乎无法可靠地工作。我曾尝试使用真实的设备,但是它永远无法在它们上运行,而使用文件进行的实验只能在我的64位设备上运行,而不能在我的32位设备上运行。

首先,做一些准备:

$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile

这不起作用:

$ 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

这适用于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

如何可靠地做到这一点?


笔记

我看了一下缓冲等问题,看了看pvbuffermbuffer。我只能让后者使用所需的缓冲区大小。

使用中间存储是解决始终存在的问题的明显方法,但是在没有足够的备用容量时不可行。

测试运行带有mbuffer版本20140302的Arch Linux的平台。


我不认为它可以解决问题,但是出于好奇,为什么要使用它mbuffer?为什么不dd使用一次性读取块设备的全部内容dd bs=102760448呢?当然,一种或另一种方式将其缓冲在RAM中。
Celada 2014年

@Celada-100MB的示例只是一个示例。例如,一次性读取1TB并不是一个好主意。
starfry 2014年

2
啊,我现在明白了,谢谢。在mbuffer实际上应该迫使第二dd落后于第一,你只需要足够的内存来缓存移位的大小。太糟糕了dd,不支持按向后顺序读取和写入块,因为这样可以消除问题!
Celada 2014年

您没有列出如何计算第二个md5sum
psusi 2014年

@psusi,第二个md5由mbuffer输出(其-H参数启用此功能)。
starfry 2014年

Answers:


2

没有缓冲区,您可能会倒退,一次只能一个块。

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

请注意,由于缺少错误检查,此示例很危险。

由于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

实际上,这不能可靠地工作,因为不能保证第一个dd设法继续读取数据,而最后一个dd(中间2M有“缓冲区”)已经在写入数据。

通过使缓冲区之间的间隔显着增大,可以大大增加机会,但是即使如此,它也不可靠。

不幸的是,我不知道具有最小填充属性的良好缓冲区程序。只要缓冲区中的安全裕度小于安全裕度,就需要停止输出。


我接受了这一点,因为它通过演示如何dd使用来回答了原始问题。不过,我认为,真正的解决方案不是使用dd而是选择旨在向后运行的东西ddrescue。我已经在答案中描述了一种方法。
starfry 2014年

1
@starfry:当然,一个能做到的程序将是一个不错的解决方案。但是,我对此不确定ddrescue。如果它期望在不同的设备上运行,则不是这样,您必须欺骗它接受您的论点。内部也可能没有“最小缓冲区填充”属性(由于不需要使用其他设备),因此它又可能破坏您的数据。您必须检查源代码是否实际上是为您的用例设计的。
弗罗斯特斯2014年

1

您正在读取4096个块,然后将这些4096个块写入磁盘的下一个4096个块,从而在读取前4096个块中将其覆盖。开始任何写入之前,您需要读取8129个块才能获得第二个4096,然后在读取下一个4096之前只需要写入4096个块即可。

您没有提到这是哪种文件系统。如果它是ext [234],并且您具有e2fsprogs的最新版本,则可以使用e2image -ra -O 512 /dev/sdj2。这还有一个额外的好处,就是足够聪明以跳过卷中的可用空间。


阅读时这很有意义,我将在此基础上再看一遍。但这并不能解释为什么它可以在测试文件上工作。
starfry 2014年

关于文件系统,您是指包含我的测试文件的文件系统吗?但这ext4只是块设备复制,任何文件系统都应该无关紧要。
starfry 2014年

@starfry,我知道以通用方式执行此操作的唯一方法是使用Emmanuel建议的算法(从末尾开始工作),这是gparted所做的。
psusi 2014年

重新设置块的大小,我尝试了更大的块(我应该在问题中写出来)。我发现即使是64K扇区缓冲区,它也变得更加可靠。可靠的解决方案是向后运行,这是dd行不通的。
starfry 2014年

1

一个可靠的解决方案要求您确保没有任何内容写入可能尚未读取的区域,而实现此目标的唯一真实方法是反向执行复制。

ddrescue工具可以反方向工作,但拒绝在输入和输出相同的情况下运行。但是,可以通过复制设备节点来欺骗它。

我已经进行了一些快速实验,它似乎可以正常工作。命令行是:

$ ddrescue -f -R -s 200704s -o 4096s /dev/sdj11 /dev/sdj11_copy

参数是

  • -f 需要强制它写入现有的输出设备
  • -R 告诉它以相反的方向工作
  • -s告诉它要复制多少输入(我使用s后缀指定扇区数)
  • -o告诉它在写入之前先在输出设备中查找(再次在s后缀中指定为扇区)
  • /dev/sdj11 是要读取的块设备
  • /dev/sdj11_copy 是要写入的块设备

我创建/dev/sdj11_copymknod以匹配的参数/dev/sdj11

我只做了一些非常快速的测试,但这确实可以复制原始设备。它不适用于文件(我无法欺骗它超出文件相同的范围)

这没有回答我最初提出的问题,该问题问如何实现这一目标,dd但是我认为,在阅读了其他答案之后,答案是dd不能做到这一点。


如果ddrescue在这种情况下发现坏块怎么办?如果它跳到磁盘的另一个区域(以避免坏块)并继续从那里复制,它将再次覆盖尚未复制的部分数据。如果它不希望与同一设备一起使用,则没有理由采取任何特殊措施来防止各种可能的数据损坏情况。
弗罗斯特斯2014年

我同意这是一个潜在的问题,但是我没有研究极端情况,因为我能够使用它来完成我需要的工作。有一些ddrescue选项可以限制其尝试恢复不良数据的尝试,但我尚未考虑使用它们。
starfry 2014年

如果输入和输出相同,则它拒绝操作的事实可能很好地表明了它是不安全的。
psusi 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.