cat*_*cat 24 pipe compression gzip dd
我有 200 GB 的可用磁盘空间、16 GB 的 RAM(其中约 1 GB 被桌面和内核占用)和 6 GB 的交换空间。
我有一个 240 GB 的外部 SSD,使用了 70 GB 1,其余的免费,我需要将其备份到我的磁盘。
通常,我会dd if=/dev/sdb of=Desktop/disk.img
先创建磁盘,然后对其进行压缩,但是首先制作映像不是一种选择,因为这样做需要比我拥有的磁盘空间多得多的磁盘空间,即使压缩步骤会导致可用空间被压缩,因此最终存档可以很容易地放在我的磁盘上。
dd
默认情况下写入 STDOUT,并且gzip
可以从 STDIN 读取,所以理论上我可以写入dd if=/dev/sdb | gzip -9 -
,但gzip
读取字节所需的时间比dd
产生它们的时间要长得多。
来自man pipe
:
写入管道写端的数据由内核缓冲,直到从管道的读端读取。
我将 a 想象|
成一个真正的管道——一个应用程序将数据推入,另一个应用程序尽可能快地从管道队列中取出数据。
当左侧的程序写入的数据比管道的另一侧希望处理的数据多时,该怎么办?它会导致极端的内存或交换使用,还是内核会尝试在磁盘上创建一个 FIFO,从而填满磁盘?或者SIGPIPE Broken pipe
如果缓冲区太大它会失败吗?
基本上,这归结为两个问题:
注 1:我不能仅仅复制前 70 个使用的 GB 并期望获得一个工作系统或文件系统,因为碎片和其他需要完整内容完整的东西。
Ste*_*itt 20
dd
一次一个块地读取和写入数据,并且它只有一个块未完成。所以
valgrind dd if=/dev/zero status=progress of=/dev/null bs=1M
Run Code Online (Sandbox Code Playgroud)
显示dd
使用大约 1MB 的内存。您可以调整块大小,然后放置valgrind
,以查看对dd
速度的影响。
当您通过管道进入 时gzip
,dd
只需减慢速度以匹配gzip
的速度。它的内存使用量不会增加,也不会导致内核将缓冲区存储在磁盘上(内核不知道如何做到这一点,除了通过交换)。只有当管道的一端死亡时才会发生破裂的管道;看到signal(7)
和write(2)
了解详情。
因此
dd if=... iconv=fullblock bs=1M | gzip -9 > ...
Run Code Online (Sandbox Code Playgroud)
是一种安全的方式来做你所追求的事情。
管道时,如果读取进程跟不上,写入进程最终会被内核阻塞。你可以通过运行看到这一点
strace dd if=/dev/zero bs=1M | (sleep 60; cat > /dev/null)
Run Code Online (Sandbox Code Playgroud)
你会看到dd
读取 1MB,然后发出一个write()
在那里等待一分钟sleep
运行的。这就是管道的两端如何平衡:如果写入过程太快,内核会阻止写入,如果读取过程太快,它会阻止读取。
fro*_*utz 18
从技术上讲,您甚至不需要dd
:
gzip < /dev/drive > drive.img.gz
Run Code Online (Sandbox Code Playgroud)
如果确实使用dd
,则应始终使用大于默认块大小的方法,dd bs=1M
否则会遇到系统调用地狱(dd
的默认块大小为 512 字节,因为它read()
s 和write()
s 是4096
syscalls per MiB
,开销太大)。
gzip -9
使用更多的 CPU 而很少显示它。如果gzip
正在减慢您的速度,请降低压缩级别,或使用不同(更快)的压缩方法。
如果您正在执行基于文件的备份而不是dd
图像,您可以有一些逻辑来决定是否完全压缩(对于各种文件类型这样做没有意义)。dar
( tar
alternative`) 是一个可以选择这样做的例子。
如果您的可用空间为零(因为它是一个 SSD 在 TRIM 之后可靠地返回零并且您运行fstrim
并删除了缓存),您还可以使用dd
withconv=sparse
标志来创建一个未压缩的、可循环安装的、稀疏的图像,该图像对零区域使用零磁盘空间. 要求图像文件由支持稀疏文件的文件系统支持。
或者,对于某些文件系统,存在只能对使用过的区域进行映像的程序。