Pipe

不會溢出到硬體資源中的動態流壓縮?

  • June 19, 2017

我有 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 使用的 GB 並期望得到一個工作系統或文件系統,因為碎片和其他需要完整內容完整的事情。

從技術上講,您甚至不需要dd

gzip < /dev/drive > drive.img.gz

如果你確實使用dd,你應該總是使用大於預設的塊大小,dd bs=1M或者遭受系統呼叫地獄(dd的預設塊大小是 512 字節,因為它read()s 和write()s 是4096每個系統呼叫MiB,太多的成本)。

gzip -9使用了更多的 CPU,幾乎沒有什麼可以展示的。如果gzip速度變慢,請降低壓縮級別,或使用不同(更快)的壓縮方法。

如果您正在執行基於文件的備份而不是dd圖像,則可以有一些邏輯來決定是否完全壓縮(對於各種文件類型這樣做沒有意義)。dar( taralternative`) 是一個可以選擇這樣做的例子。

如果您的可用空間為零(因為它是一個在 TRIM 之後可靠地返回零並且您執行fstrim並丟棄記憶體的 SSD),您還可以使用ddwith conv=sparseflag 創建一個未壓縮、可循環安裝的稀疏映像,該映像使用零磁碟空間用於零區域. 要求圖像文件由支持稀疏文件的文件系統支持。

或者,對於某些文件系統,存在只能對已使用區域進行映像的程序。

dd一次讀取和寫入一個塊的數據,並且它只有一個塊未完成。所以

valgrind dd if=/dev/zero status=progress of=/dev/null bs=1M

顯示dd使用大約 1MB 的記憶體。您可以使用塊大小和 dropvalgrind來查看對dd速度的影響。

當您進入gzip時,dd只需放慢速度以匹配gzip的速度。它的記憶體使用不會增加,也不會導致核心將緩衝區儲存在磁碟上(核心不知道如何做到這一點,除非通過交換)。只有當管道的一端死亡時才會發生破裂的管道;請參閱signal(7)write(2)了解詳情。

因此

dd if=... iconv=fullblock bs=1M | gzip -9 > ...

是一種安全的方式來做你所追求的。

管道時,如果讀取程序跟不上,寫入程序最終會被核心阻塞。您可以通過執行看到這一點

strace dd if=/dev/zero bs=1M | (sleep 60; cat > /dev/null)

您會看到它dd讀取 1MB,然後發出一個write()在執行時坐在那裡等待一分鐘的問題sleep。這就是管道兩端的平衡方式:如果寫入過程太快,核心會阻止寫入,如果讀取過程太快,它會阻止讀取。

引用自:https://unix.stackexchange.com/questions/371789