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 毫秒。但如果进程的目标速率限制小于每秒一页,则不会;在在这种情况下,它将应用严格的速率限制。)
(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。
查看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().