Linux

為什麼 gunzip 到 dd 管道最後會變慢?

  • September 24, 2018

我的命令:

gunzip -c serial2udp.image.gz |
sudo dd of=/dev/mmcblk0 conv=fsync,notrunc status=progress bs=4M

我的輸出:

15930949632 bytes (16 GB, 15 GiB) copied, 1049 s, 15.2 MB/s    

0+331128 records in
0+331128 records out
15931539456 bytes (16 GB, 15 GiB) copied, 1995.2 s, 8.0 MB/s

卡:SanDisk Ultra 32GB MicroSDHC Class 10 UHS 儲存卡速度高達 30MB/s

分佈:16.0.4 xenial,xfce

核心版本:4.13.0.37-generic

我明白從我所讀到的內容來看,花 17 分鐘似乎是合理的。使用塊大小似乎並沒有太大區別(bs=100M 仍然表現出具有相似時間戳的這種行為)。為什麼更新掛起並且在另外 16 分鐘內沒有生成完成的報告?

iotop 告訴我此時 mmcqd/0 仍在後台執行(以 99% 的 IO),所以我認為某處有一個記憶體佔用了最後的 5MB,但我認為 fsync 應該確保不會發生這種情況iotop 顯示此時 dd 也沒有交通路口。ctrl-c 幾乎沒用,我不想在寫入驅動器後損壞驅動器。

我認為某處有一個記憶體保留了最後的 5MB,但我認為 fsync 應該確保不會發生這種情況

conv=fsync表示通過呼叫fsync- 在dd寫入所有數據之後寫回任何記憶體。掛在最後正是它會做的事情。

當輸出文件比輸入文件慢時,寫入的數據dd會堆積在記憶體中。核心記憶體有時會填滿系統 RAM 的很大一部分。這會導致非常誤導的進度資訊。您的“最後 5MB”只是dd展示進度的人工製品。

如果您的系統確實記憶體了大約 8GB(即 16GB 寫入數據的一半),那麼我認為您要麼必須有大約 32GB 的 RAM,要麼一直在擺弄某些核心選項。請參閱下面的 lwn.net 連結。我同意 15 分鐘內沒有獲得任何進度資訊是非常令人沮喪的。

dd您可以使用其他命令。如果您想dd顯示更準確的進度,您可能不得不接受更多的複雜性。我希望以下內容可以在不降低您的表現的情況下發揮作用,儘管現實可能與我有不同的想法。

gunzip -c serial2udp.image.gz |
dd iflag=fullblock bs=4M |
sudo dd iflag=fullblock oflag=direct conv=fsync status=progress bs=4M of=/dev/mmcblk0
  • oflag=direct iflag=fullblock避免堆積核心記憶體,因為它完全繞過它。
  • iflag=fullblock在這樣的命令 AFAIK 中是必需的(例如,因為您正在從管道讀取並使用直接 IO 寫入)。缺失的影響fullblock是另一個不幸的複雜性dd。這個網站上的一些文章使用這個來爭論你應該總是更喜歡使用不同的命令。不過,很難找到另一種直接或同步 IO 的方法。
  • conv=fsync仍應使用,以寫回設備記憶體。
  • dd我在 之後添加了一個額外的gunzip,以與磁碟寫入並行緩衝解壓縮的輸出。這是使性能變得複雜oflag=directoflag=sync有點複雜的問題之一。普通 IO(非直接、非同步)不應該需要這個,因為它已經被核心記憶體緩衝了。如果您正在寫入具有 4M 寫回記憶體的硬碟驅動器,您也可能不需要額外的緩衝區,但我不認為 SD 卡有那麼多。

您也可以使用oflag=direct,sync(而不是需要conv=fsync)。如果您有一個帶有數百兆記憶體的奇怪輸出設備,這可能對獲得良好的進度資訊很有用。但通常我認為oflag=sync這是性能的潛在障礙。

有一篇 2013 年的文章https://lwn.net/Articles/572911/提到了像您這樣的長達一分鐘的延遲。許多人認為這種記憶體幾分鐘的寫回數據的能力是不可取的。問題是記憶體大小的限制被不加選擇地應用於快速和慢速設備。請注意,核心測量設備速度並非易事,因為它因數據位置而異。例如,如果記憶體的寫入分散在隨機位置,硬碟驅動器將需要更長的時間來重複移動寫入頭。

為什麼更新掛起

fsync()是適用於整個範圍的單個系統呼叫文件設備。它在完成之前不會返回任何狀態更新。

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