為什麼第一次呼叫 fsync() 時 rename() 需要更長的時間?
在這個測試中,為什麼第一次呼叫 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: make fsync to sync parent dir in no-journal for real this time
如果您對後續時間感到驚訝
sync
,我可以確認dpkg
用全域 sync() 呼叫替換單個 fsync() 呼叫的修補似乎可以將總時間降低到大約 13 秒。而且我在我的系統上沒有發現任何不足之處。dpkg
由於其他潛在的副作用,剛剛停止使用這種方法。$$ 1 $$$$ 2 $$
根據送出描述,我預計 rename() 延遲是由Btrfs: sync log after logging new name引起的。這是在核心 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_log_new_name()
BTRFS_NEED_LOG_SYNC
最後一行返回的觸發。我發現這一點的方法是使用offcputime。它通過堆棧跟踪聚合等待時間。堆棧跟踪如下所示:
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