为什么先调用 fsync() 时 rename() 需要更长的时间?

为什么先调用 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:这次让 fsync 同步无日志中的父目录

如果您对跟踪sync时间感到惊讶,我可以确认,通过修补dpkg以使用全局sync() 调用替换单个fsync() 调用似乎可以将总体时间降低到大约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

相关内容