对 dd 写入速度和 VFS 页缓存感到困惑

对 dd 写入速度和 VFS 页缓存感到困惑

在阅读了一些有关 Linux VFS 页面缓存和可调参数的文章后,dirty_ratio我的印象是页面缓存将作为读取和写入缓存层运行。

但是使用下面的简单测试可以很好地提高位于页面缓存中但似乎不适用于写入的文件的读取速度。

例如

清除缓存并写入文件。

# swapoff -a
# echo 3 > /proc/sys/vm/drop_caches

# dd if=/dev/zero of=/home/flo/test bs=1M count=30
30+0 records in
30+0 records out
31457280 bytes (31 MB) copied, 0.182474 s, 172 MB/s

检查文件是否确实在页面缓存中

# vmtouch /home/flo/test 
           Files: 1
     Directories: 0
  Resident Pages: 7680/7680  30M/30M  100%
         Elapsed: 0.000673 seconds

从文件中读取以确认实际上来自缓存。

# dd if=/home/flo/test of=/dev/null bs=1M count=30
30+0 records in
30+0 records out
31457280 bytes (31 MB) copied, 0.00824169 s, 3.8 GB/s

删除缓存并再次读取以证明速度差异。

# echo 3 > /proc/sys/vm/drop_caches
# dd if=/home/flo/test of=/dev/null bs=1M count=30
30+0 records in
30+0 records out
31457280 bytes (31 MB) copied, 0.132531 s, 237 MB/s

由于我没有将 DIRECT_IO 与 dd 一起使用,因此我希望将页面缓存用作写回类型的缓存。并且基于dirty_ratiodirty_expire_centiseconds......最终数据将被提交到磁盘。

有人可以解释一下 VFS 如何以不同的方式处理读取和写入过程,特别是在写入过程中,以及为什么没有速度增益。

有没有什么方法可以让 vfs 在写入缓存方面更加积极,因此它的行为更像是在 raid 控制器上找到的写回缓存。

谢谢

答案1

man ext4 有关于 (no) 选项的介绍auto_da_alloc

许多损坏的应用程序不使用 fsync()...

这背后似乎有一个很长的故事(一些关于数据丢失的悲剧)。这与延迟分配文件系统块。 Ext2/3 没有这个功能,但它不仅是 ext4 的一个非常重要的功能。

如果应用程序没有同步,用户也没有手动同步,内核也没有在 30 秒后同步,那么当涉及到某些文件重写时,文件系统最好立即执行同步。否则,有了DA,停电时很容易发生不好的事情。比失去最后的更改更糟糕的事情。

如果没有conv=notruncatedd 命令,覆盖时就像“应用程序”一样。它必须删除现有文件才能创建新文件,否则如果现有文件更长,您将得到混合文件。

mount -o remount,noauto_da_alloc ...可以在 ext4 上关闭此行为。现在块写入可以在截断后很长时间内完成。

下一个攻击性程度将提高定期写回的 30 秒到期时间和 5 秒检查间隔(/proc/sys/vm/ 中的 dirty_..._centisecs 值)。使用默认的 30/5 时,一些新文件将在半分钟后写入,除非您删除速度非常快。

VFS 对未使用页面的攻击性越强,文件系统对块设备的攻击性就越小。


挂载选项和写回参数

]# findmnt --real
TARGET       SOURCE     FSTYPE OPTIONS
/            /dev/sda3  ext4   rw,relatime,noauto_da_alloc
|-/root/sda1 /dev/sda1  ext2   rw,relatime
`-/root/16   /dev/sda16 ext4   rw,relatime

在这样的设置中,覆盖会立即在 sda16 上同步,但不会在其他两个上同步。

目前我(想我)完全关闭了定期写回。

]# grep '' /proc/sys/vm/*centisecs
/proc/sys/vm/dirty_expire_centisecs:720000
/proc/sys/vm/dirty_writeback_centisecs:0

现在我终于收集脏页了:

]# grep nr_dirty /proc/vmstat 
nr_dirty 10077
nr_dirty_threshold 437320
nr_dirty_background_threshold 174671

尝试收集它们并以某种方式接近默认的 10% 背景比例 - 昨天当我进入挂起到内存睡眠状态时,我得到了同步。这是有道理的:谁愿意和 MB 的脏页一起睡觉?

mm/writeback.c细节非常复杂,评论本身就说明了这一点。一个问题是不要错过“1000个dd立即开始脏”时的节流点。从长远来看,“回写”的目标似乎是 10% 左右。正如我上面的示例所示,在正常(最少)使用情况下,这 10%(总/可用 RAM)需要很长时间才能填满。一分钟浏览大约会弄脏 1000 个页面。


理论结束后,具体证明

我在上面列出的两个文件系统上测试了 10 个块:

]# dd if=/dev/zero of=test10  bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0076396 s, 1.4 GB/s

]# dd if=/dev/zero of=test10  bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.00514406 s, 2.0 GB/s

-> 在根分区(sda3,上面)上使用 noauto_da_alloc 覆盖速度更快。

在默认安装的 ext4(上面的 sda16)上它会变慢:

]# rm test10 

]# dd if=/dev/zero of=test10  bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.00800839 s, 1.3 GB/s

]# dd if=/dev/zero of=test10  bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0740824 s, 142 MB/s

...因为整个覆盖是同步的,如图vmstat 1 |cut...所示:

    0     0
    0     0
    0     0
-----io----
   bi    bo
    0 10240
    0     0
    0     0

手动sync延迟分配

它的好处是:您可以在需要时执行此操作,并且可以对单个文件执行此操作,也可以对整个驱动器执行此操作。

另外:卸载、关闭(和暂停)也包括在内。

坏事是:当(覆盖)写入和同步之间发生崩溃/电源故障时,零长度“损坏”风险。这意味着您实际上只能安全地保存您放在一个或两个外部存储中的内容。


我找不到底线。没有简单的解决方案,只有很长(但至少合乎逻辑)的解释。

答案2

为了看到快速行为,我必须rm test首先做。例如,我看到dd报告为 1GB/s,而不是 150MB/s。

参考:

虽然参考文献只解释了为什么我想尝试这个,但它实际上并没有解释为什么它会导致 IO 阻塞。

在我的电脑上,阻塞似乎只发生在新的 WBT(“写回限制”)代码中......该代码于 2016 年添加,你问了你的问题。我还没分析过为什么它会导致这个。当 WBT 被禁用时,它就会消失。

我的内核版本是4.18.16-200.fc28.x86_64.

strace -T显示所有时间都花在 close() 上,这对我来说最有意义。我perf也尝试使用。它没有按预期工作,但它显示了堆栈跟踪,例如

dd 17068 [003] 475165.381526:       sched:sched_switch: dd:17068 [120] T ==> kworker/3:1H:19326 [100]
    ffffffffa390c172 __sched_text_start+0x352 ([kernel.kallsyms])
    ffffffffa390c172 __sched_text_start+0x352 ([kernel.kallsyms])
    ffffffffa390c6a8 schedule+0x28 ([kernel.kallsyms])
    ffffffffa30def32 io_schedule+0x12 ([kernel.kallsyms])
    ffffffffa3461ed7 wbt_wait+0x337 ([kernel.kallsyms])
    ffffffffa342ee33 blk_queue_bio+0x123 ([kernel.kallsyms])
    ffffffffa342d114 generic_make_request+0x1a4 ([kernel.kallsyms])
    ffffffffa342d3c5 submit_bio+0x45 ([kernel.kallsyms])
    ffffffffa3377d78 ext4_io_submit+0x48 ([kernel.kallsyms])
    ffffffffa335da2c ext4_writepages+0x70c ([kernel.kallsyms])
    ffffffffa3209311 do_writepages+0x41 ([kernel.kallsyms])
    ffffffffa31f808e __filemap_fdatawrite_range+0xbe ([kernel.kallsyms])
    ffffffffa334b9ec ext4_release_file+0x6c ([kernel.kallsyms])
    ffffffffa32a9d4e __fput+0xae ([kernel.kallsyms])
    ffffffffa30cf474 task_work_run+0x84 ([kernel.kallsyms])
    ffffffffa3003e6e exit_to_usermode_loop+0xce ([kernel.kallsyms])
    ffffffffa300425d do_syscall_64+0x14d ([kernel.kallsyms])
    ffffffffa3a00088 entry_SYSCALL_64_after_hwframe+0x44 ([kernel.kallsyms])
        7fcca3a60654 __close+0x14 (/usr/lib64/libc-2.27.so)

这提醒我当前正在测试deadlineI/O 调度程序,并启用了 WBT(“写回限制”)。禁用 WBT(包括切换到不兼容的 CFQ)让我再次获得快速行为!

perf我用来看到这个的命令是:

sudo perf record -e sched:sched_stat_sleep -e sched:sched_switch -e sched:sched_process_exit -gP -o ~/perf.data dd if=/dev/zero of=test bs=1M count=30
sudo perf script -i ~/perf.data | cat

答案3

只是不要使用dd。例如,使用cp,您将获得用于写入的页面缓存。

相关内容