回写缓存(`dirty`)似乎被限制在甚至小于dirty_background_ratio。它受到什么限制?这个限制是如何计算的?

sou*_*edi 6 linux cache sysctl linux-kernel atop

我一直在测试 Linux 4.18.16-200.fc28.x86_64。根据free -h.

我有vm.dirty*sysctl 的默认值。 dirty_background_ratio是 10,现在dirty_ratio是 20。根据我读过的所有内容,我希望 Linux 在达到 RAM 的 10% 时开始写出脏缓存:0.77G。当脏缓存达到 RAM 的 20%:1.54G 时,缓冲的 write() 调用应该阻塞。

我跑过去dd if=/dev/zero of=~/test bs=1M count=2000看着dirty田野atop。当dd命令运行时,该dirty值稳定在 0.5G 左右。这明显低于脏背景阈值(0.77G)!怎么会这样?我错过了什么?

dirty_expire_centisecs是 3000,所以我认为这不是原因。我什至尝试降低dirty_expire_centisecs到 100 和dirty_writeback_centisecs10,看看这是否是限制dirty。这并没有改变结果。

作为调查的一部分,我最初写下了这些观察结果:为什么在 2013 年报告了“USB 记忆棒停顿”问题?为什么现有的“No-I/O 脏节流”代码没有解决这个问题?


我知道在两个阈值之间的中间 - 15% = 1.155G - write() 调用开始在曲线上受到限制(延迟)。但是在这个天花板之下时不会增加延迟;允许生成脏页的进程“自由运行”。

据我了解,节流旨在将脏缓存保持在 15% 或以上,并防止达到 20% 的硬限制。它并不为每种情况提供保证。但我正在用一个dd命令测试一个简单的案例;我认为它应该简单地限制 write() 调用以匹配设备实现的写出速度。

(没有简单的保证,因为有一些复杂的异常。例如,油门代码将其施加的延迟限制为最大 200 毫秒。但如果进程的目标速率限制小于每秒一页,则不会;在在这种情况下,它将应用严格的速率限制。)

  • 文档/sysctl/vm.txt -- Linux v4.18
  • 无 I/O 脏节流-- 2011 LWN.net。
  • (dirty_background_ratio +dirty_ratio)/2 个脏数据总共......是我们开始限制进程时的脏数据量 -- Jan Kara,2013

  • 用户会注意到,一旦跨越全局(后台 + 脏)/2=15% 阈值,应用程序将受到限制,然后在 17.5% 左右平衡。在补丁之前,行为只是将其限制在 20% 可脏内存

    -- 提交 143dfe8611a6,“写回:IO-less balance_dirty_pages()

  • 默认情况下,内存管理子系统将尝试将脏页限制为系统内存的最大 15%。有一个叫做 balance_dirty_pages() 的“神奇函数”,如果需要的话,它会限制进程弄脏很多页面,以匹配页面被弄脏的速度和它们可以被清理的速度。”-回写和控制组,2015 LWN.net。

  • Linux 4.18.16 中的balance_dirty_pages()

sou*_*edi 5

查看Documentation/sysctl/vm.txt

脏比率

包含,作为包含空闲页面和可回收页面的总可用内存的百分比,生成磁盘写入的进程本身将开始写出脏数据的页面数。

总可用内存不等于总系统内存

可用内存在global_dirtyable_memory() 中计算。它等于可用内存量加上页面缓存。它不包括可交换页面(即匿名内存分配,不受文件支持的内存)。

此行为自Linux 3.14 (2014)起适用。在此更改之前,可交换页面包含在 global_dirtyable_memory() 总数中。

运行dd命令时的示例统计信息:

$ while true; do grep -E '^(Dirty:|Writeback:|MemFree:|Cached:)' /proc/meminfo | tr '\n' ' '; echo; sleep 1; done
MemFree:         1793676 kB Cached:          1280812 kB Dirty:                 4 kB Writeback:             0 kB
MemFree:         1240728 kB Cached:          1826644 kB Dirty:            386128 kB Writeback:         67608 kB
MemFree:         1079700 kB Cached:          1983696 kB Dirty:            319812 kB Writeback:        143536 kB
MemFree:          937772 kB Cached:          2121424 kB Dirty:            312048 kB Writeback:        112520 kB
MemFree:          755776 kB Cached:          2298276 kB Dirty:            389828 kB Writeback:         68408 kB
...
MemFree:          136376 kB Cached:          2984308 kB Dirty:            485332 kB Writeback:         51300 kB
MemFree:          101340 kB Cached:          3028996 kB Dirty:            450176 kB Writeback:        119348 kB
MemFree:          122304 kB Cached:          3021836 kB Dirty:            552620 kB Writeback:          8484 kB
MemFree:          101016 kB Cached:          3053628 kB Dirty:            501128 kB Writeback:         61028 kB
Run Code Online (Sandbox Code Playgroud)

最后一行显示大约 3,150,000 kB 的“可用”内存,以及总共 562,000 kB 的数据正在写回或等待写回。这使它成为 17.8%。虽然看起来这个比例在这个水平上下波动,但更多时候是接近 15%。 编辑:虽然这些数字看起来更近,但请不要相信这种方法。它仍然不是正确的计算,它可能会给出非常错误的结果。请参阅此处的后续内容。


我发现这很难:

我注意到balance_dirty_pages() 中有一个跟踪点,可用于“分析节流算法的动态”。所以我用了perf

$ sudo perf list '*balance_dirty_pages'

List of pre-defined events (to be used in -e):

  writeback:balance_dirty_pages                      [Tracepoint event]
...
$ sudo perf record -e writeback:balance_dirty_pages dd if=/dev/zero of=~/test bs=1M count=2000
$ sudo perf script
Run Code Online (Sandbox Code Playgroud)

它表明dirty(以 4096 字节页面衡量)低于我的预期,因为setpoint低。我追踪了代码;这意味着freerun跟踪点定义中必须有一个类似的低值,该值设置为(thresh + bg_thresh) / 2... 并返回到global_dirtyable_memory().