不会溢出到硬件资源的动态流压缩?

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. 将多于一次读取的数据推入管道的含义和结果是什么?
  2. 将数据流压缩到磁盘而不将整个未压缩的数据流放在磁盘上的可靠方法是什么?

注 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速度的影响。

当您通过管道进入 时gzipdd只需减慢速度以匹配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运行的。这就是管道的两端如何平衡:如果写入过程太快,内核会阻止写入,如果读取过程太快,它会阻止读取。

  • @cat 这是自动的;`dd` 调用 `write()` 将数据放入管道。`write()` 实际上将控制权转移给内核,以便它可以操作管道内存。如果内核看到管道已满,它将等待(“阻塞”)直到管道有足够的空间。只有到那时 `write()` 才会调用完成并将控制权转移回 `dd`,然后它会再次将数据写入管道。 (9认同)

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 是4096syscalls per MiB,开销太大)。

gzip -9使用更多的 CPU 而很少显示它。如果gzip正在减慢您的速度,请降低压缩级别,或使用不同(更快)的压缩方法。

如果您正在执行基于文件的备份而不是dd图像,您可以有一些逻辑来决定是否完全压缩(对于各种文件类型这样做没有意义)。dar( taralternative`) 是一个可以选择这样做的例子。

如果您的可用空间为零(因为它是一个 SSD 在 TRIM 之后可靠地返回零并且您运行fstrim并删除了缓存),您还可以使用ddwithconv=sparse标志来创建一个未压缩的、可循环安装的、稀疏的图像,该图像对零区域使用零磁盘空间. 要求图像文件由支持稀疏文件的文件系统支持。

或者,对于某些文件系统,存在只能对使用过的区域进行映像的程序。

  • 叹。每次提到 `dd` 块大小,人们都会吹毛求疵。`gzip` 是 cpu 密集型也是我回答的一部分,好吗?抱歉,我不同意“可以忽略不计”。使用 `gzip -9` 可能只为每场演出增加 1-2 秒(但在处理数百场演出时仍然需要几分钟),但是使用 `lzop -1` 的建议是每场演出 1 秒,而每场演出 4 秒。在土豆(单核虚拟服务器)上测试。* 向 `dd` 添加一个合理的块大小不需要任何成本并且零缺点。不要挑剔。就去做吧。* ymmv (9认同)

Ulr*_*arz 9

除了性能之外没有任何负面影响:管道有一个缓冲区,通常是 64K,之后,对管道的写入将简单地阻塞,直到gzip读取更多数据。


pjc*_*c50 8

回答有关它如何工作的实际问题:“如果左侧的程序写入的数据比管道的另一侧希望处理的数据更快,该怎么办?”

这不会发生。管道中有一个相当小的、大小有限的缓冲区;请参阅管道缓冲区有多大?

一旦管道缓冲区已满,发送程序就会阻塞。当它进行写调用时,内核不会将控制权返回给程序,直到数据写入缓冲区。这为读取程序提供了清空缓冲区的 CPU 时间。