當輸出文件為 /dev/null 時,為什麼 tar 似乎會跳過文件內容?
我有一個目錄,其中包含超過 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
應該在所有其他情況下都應避免。