为什么将 `tar` 管道传输到 `dd` 直到磁盘已满才停止?

为什么将 `tar` 管道传输到 `dd` 直到磁盘已满才停止?

我有一个单个磁盘映像的 tar 存档。该 tar 文件内的图像大小约为 4GB。我通过管道将tar xfinto的dd输出写入到 SD 卡中。直到卡已满,磁盘转储才会停止。这是我的 shell 会话:

$ ls -l disk.img.tgz
-rw-r--r-- 1 confus confus 192M Okt  5 00:53

$ tar -tvf disk.img.tgz
-rw-r--r-- root/root 4294968320 2018-10-05 00:52 disk.img

$ lsblk -lb /dev/sdc
NAME MAJ:MIN RM        SIZE RO TYPE MOUNTPOINT
sdc    8:32   1 16022241280  0 disk

$ tar zxf disk.img.tgz -O | sudo dd status=progress conv=sync bs=1M of=/dev/sdc
[sudo] password for user: 
15992881152 bytes (16 GB, 15 GiB) copied, 212 s, 75,4 MB/s 
dd: error writing '/dev/sdc': No space left on device
0+15281 records in
15280+0 records out
16022241280 bytes (16 GB, 15 GiB) copied, 217,67 s, 73,6 MB/s

为什么?在 hit 将 4GB 映像写入 16GB 购物车后,它应该停止,并且永远不会用完空间!

答案1

那是因为你做错了。

您正在使用bs=1M但从标准输入、管道读取的读数将会较小。事实上,根据 dd 的说法,你没有得到一个完整的阅读。

然后你就可以用conv=sync零来补充不完整的读取。

0+15281 records in
15280+0 records out

dd收到 0 个完整读取和 15281 个不完整读取,并写入 15280 个完整块(conv=同步零填充)。因此输出比输入大得多,直到没有剩余空间为止。

   sync   pad  every  input  block  with  NULs to ibs-size; when used with
          block or unblock, pad with spaces rather than NULs

要解决此问题,您可以删除conv=sync并添加iflag=fullblock.


为了说明这一点,请考虑yes,默认情况下会输出无限的“y\ny\ny\n”。

$ yes
y
y
y
^C
$ yes | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*

dd bs=1M conv=sync看起来像这样:

$ yes | dd bs=1M conv=sync | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
0001e000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
00112000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

因此它得到一个不完整的“y\ny\ny\n”块(0x00000 - 0x1e000,122880 字节),然后将剩余的 1M 写入零(0x01e000 - 0x100000,925696 字节)。在大多数情况下,您不希望这种情况发生。无论如何,结果是随机的,因为您无法真正控制每次读取的不完整程度。像这里第二次读取的不再是 122880 字节而是 73728 字节。

dd conv=sync很少有用,即使在受欢迎的情况下也是如此,比如当你遇到读取错误时写零,事情会变得非常糟糕。

相关内容