Tar

當輸出文件為 /dev/null 時,為什麼 tar 似乎會跳過文件內容?

  • April 14, 2019

我有一個目錄,其中包含超過 400 GiB 的數據。我想檢查所有文件是否可以正確讀取,所以我想到的一個簡單方法是將tar其放入/dev/null. 但相反,我看到了以下行為:

$ time tar cf /dev/null .

real    0m4.387s
user    0m3.462s
sys     0m0.185s
$ time tar cf - . > /dev/null

real    0m3.130s
user    0m3.091s
sys     0m0.035s
$ time tar cf - . | cat > /dev/null
^C

real    10m32.985s
user    0m1.942s
sys     0m33.764s

上面的第三個命令在執行了很長時間後被Ctrl+強行停止了。C此外,目前兩個命令工作時,包含的儲存設備的活動指示器.幾乎總是處於空閒狀態。使用第三個命令時,指示燈會持續亮起,表示極度忙碌。

所以看來,當tar能夠查出它的輸出文件是/dev/null,即/dev/null直接打開有tar寫入的文件句柄時,文件體就出現了跳過。(添加v選項以tar列印目錄中的所有文件為tar“紅色”。)

所以我想知道,為什麼會這樣?是某種優化嗎?如果是的話,那為什麼還要tar對這種特殊情況進行如此可疑的優化呢?

我在 Linux 4.14.105 amd64 上使用 GNU tar 1.26 和 glibc 2.27。

一個記錄在案的優化

當歸檔文件被創建到 時/dev/null,GNU tar 嘗試最小化輸入和輸出操作。Amanda 備份系統在與 GNU tar 一起使用時,具有使用此功能的初始大小調整通道。

這可能發生在各種程序中,例如,我曾經在使用cp file /dev/null;時出現過這種行為。該命令在幾毫秒後返回,而不是估計我的磁碟讀取速度。

據我記得,那是在 Solaris 或 AIX 上,但該原理適用於各種 unix-y 系統。

在過去,當程序將文件複製到某個地方時,它會在read從磁碟(或文件描述符所指的任何東西)獲取一些數據到記憶體(保證read返回時一切都在那裡)和write呼叫之間交替呼叫(它佔用記憶體塊並將內容髮送到目的地)。

但是,至少有兩種更新的方法可以實現相同的目標:

  • Linux 有系統呼叫copy_file_range(根本不能移植到其他 unix)和sendfile(有點可移植;最初打算將文件發送到網路,但現在可以使用任何目的地)。它們旨在優化傳輸;如果程序使用其中之一,很容易想像核心辨識目標是/dev/null並將系統呼叫轉換為無操作
  • 程序可以mmap用來獲取文件內容而不是read,這基本上意味著“當我嘗試訪問那塊記憶體時確保數據在那裡”而不是“確保系統呼叫返回時數據在那裡”。所以程序可以mmap獲取源文件,然後呼叫write那塊映射記憶體。但是,由於寫入/dev/null不需要訪問寫入的數據,因此永遠不會觸發“確保它在那裡”條件,導致文件也不會被讀取。

不確定 gnu tar 在檢測到它正在寫入時是否使用這兩種機制中的任何一種,以及哪一種/dev/null,但它們是任何程序在用於檢查讀取速度時應該使用| cat > /dev/null而不是執行的> /dev/null原因 - 以及為什麼| cat > /dev/null應該在所有其他情況下都應避免。

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