在這個測試中,為什麼先呼叫 fsync() 時 rename() 需要更長的時間?
環境:btrfs、機械硬碟、Debian 9 容器,在核心 5.0.17-200.fc29.x86_64 上運作。
測試命令:dpkg -r linux-image-4.9.0-9-amd64 >/dev/null 2>&1 && sync && time perf_4.9 trace --no-inherit -s dpkg $FORCE_UNSAFE_IO -i linux-image-4.9.0-9-amd64_4.9.168-1_amd64.deb && time sync
FORCE_UNSAFE_IO=""
比較vs的結果FORCE_UNSAFE_IO="--force-unsafe-io"
。
dpkg (31632), 374488 events, 100.0%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
fsync 3442 14849.586 0.002 4.314 149.959 4.11%
rename 8463 14573.509 0.003 1.722 358.675 4.80%
wait4 7 8043.762 0.004 1149.109 8028.468 99.78%
read 44025 2151.135 0.000 0.049 3.732 0.57%
open 19301 213.628 0.002 0.011 0.375 0.90%
write 7846 165.460 0.003 0.021 0.149 0.42%
sync_file_range 6834 96.513 0.001 0.014 0.822 2.20%
...
real 0m41.703s
user 0m9.709s
sys 0m6.586s
real 0m0.162s
user 0m0.000s
sys 0m0.003s
dpkg (1919), 334232 events, 100.0%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
wait4 7 8290.981 0.007 1184.426 8279.676 99.84%
read 44399 2168.096 0.000 0.049 2.146 0.50%
fsync 25 653.530 0.006 26.141 68.754 8.65%
rename 8463 522.282 0.003 0.062 69.620 22.53%
open 12467 163.671 0.002 0.013 0.217 0.97%
write 7846 160.979 0.003 0.021 0.356 0.50%
sync_file_range 3417 89.676 0.010 0.026 0.841 2.05%
...
real 0m13.498s
user 0m9.643s
sys 0m5.517s
real 0m0.146s
user 0m0.000s
sys 0m0.004s
目前的策略dpkg
(例如在 Debian 9 中)比您可能意識到的更為複雜。但我不確定這是否真的會影響這個案子。如果您想了解更多詳細信息,這個問題有一些背景:AIO fsync 可以提高 dpkg 效能嗎?
我不知道這是否相關,但我發現在某些檔案系統上 fsync() 也可以有效地同步目錄。這是為了確保在 fsync() 傳回之前新建立的檔案在磁碟上可見。我在某處讀到這在 ext2 上不會發生,但在 ext4 上確實會發生。作為部分證據,請參見ext4:這次讓 fsync 同步無日誌中的父目錄
如果您對追蹤sync
時間感到驚訝,我可以確認,透過修補將dpkg
單一 fsync() 呼叫替換為全域sync() 呼叫似乎可以將總體時間降低到大約 13 秒。我在我的系統上沒有發現任何不足之處。 dpkg
由於其他潛在的副作用,我只是停止使用這種方法。[1][2]
答案1
根據提交描述,我預計 rename() 延遲是由Btrfs:記錄新名稱後同步日誌。這是在核心 v4.19 中加入的。
使新檔案名稱的記錄(在建立硬連結或重新命名時發生)保留在日誌中。
這種方法不僅更簡單,[...]而且還為我們提供了與 ext4、xfs 和 f2fs(可能還有其他檔案系統)相同的行為。
我不相信第二句話是對的!
公平地說,我應該指出的dpkg
是,在將套件記錄為正確安裝之前,忘記了 fsync() 包含檔案的目錄。但這種 btrfs 行為與 Linux 的其他部分並不完全匹配。
我不相信 XFS 會在 rename() 中同步新的目錄條目(即故意等待它被持久化)。我對 XFS rename() 內任何同步寫入的假設部分基於此線程:https://marc.info/?l=linux-xfs&m=139863577410237&w=2
對於 ext4,我提到的證據表明fsync()可能會在新目錄條目返回之前同步它。但我不相信 ext4 的 rename() 會這樣做。
我連結到最近的討論AIO fsync() 操作,以及它們如何實現元資料更新的高效批次處理。關於假設的 AIO rename() 還沒有太多討論,因為通常的假設是 rename() 不是同步操作!
(總的來說,btrfs 對我來說有點可疑。即,我看到這個資料完整性錯誤修復是在過去的幾個版本中,而且它並不是唯一聽起來可怕的修復變更日誌對於這些版本)。
我認為 rename() 延遲必須由BTRFS_NEED_LOG_SYNC
最後一行返回的btrfs_log_new_name()。
我發現這個的方法是使用關閉CPU時間。它透過堆疊追蹤聚合等待時間。堆疊追蹤如下所示:
io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
9735954
io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
9147785
io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
4478158
io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
4376109