在之前的问答中,我展示了一个关于 dirty_ratio 的实验。
回写缓存(“dirty”)似乎被限制为甚至小于 dirty_background_ratio。它受到什么限制?这个限制是如何计算的?
我想我通过纠正我对脏率计算的理解解决了这个问题。但我重复了刚才的实验,回写缓存被限制为比我之前看到的要低。我无法解决这个问题,是什么限制了它?
我有 sysctl 的默认值vm.dirty*
。 dirty_background_ratio
是 10,dirty_ratio
是 20。“比率”是指脏页缓存(又名回写缓存)的大小,以MemFree
+的百分比表示Cached
。他们是不是百分比MemTotal
- 这就是我在上述问题中感到困惑的地方。
这些比率意味着达到 10% 会导致后台写回启动,而 20% 是写回缓存的最大大小。此外,我知道回写式缓存受到“I/O-less 脏节流”的限制。当回写高速缓存上升到 15% 以上时,使用 write() 生成脏页的进程将被“限制”。也就是说,内核导致进程在 write() 调用内休眠。因此内核可以通过控制睡眠的长度来控制回写缓存的大小。有关参考,请参阅我之前问题的答案。
但我观察到的“比率”似乎明显低于 15% 的限制阈值。一定有什么因素我错过了!为什么会发生这种情况?
在我之前的测试中,我看到的值约为 15-17.5%。
我的内核是Linux 4.18.16-200.fc28.x86_64
。
测试如下:我跑了dd if=/dev/zero of=~/test bs=1M status=progress
。同时,我监控了所达到的脏率。dd
15GB后我中断了命令。
$ while true; do grep -E '^(Dirty:|Writeback:|MemFree:|Cached:)' /proc/meminfo | tr '\n' ' '; echo; sleep 1; done
...
MemFree: 139852 kB Cached: 3443460 kB Dirty: 300240 kB Writeback: 135280 kB
MemFree: 145932 kB Cached: 3437220 kB Dirty: 319588 kB Writeback: 112080 kB
MemFree: 134324 kB Cached: 3448776 kB Dirty: 237612 kB Writeback: 160528 kB
MemFree: 134012 kB Cached: 3449004 kB Dirty: 169064 kB Writeback: 143256 kB
MemFree: 133760 kB Cached: 3449024 kB Dirty: 105484 kB Writeback: 119968 kB
MemFree: 133584 kB Cached: 3449032 kB Dirty: 49068 kB Writeback: 104412 kB
MemFree: 134712 kB Cached: 3449116 kB Dirty: 80 kB Writeback: 78740 kB
MemFree: 135448 kB Cached: 3449116 kB Dirty: 8 kB Writeback: 0 kB
例如,引用输出中的第一行:
avail = 139852 + 3443460 = 3583312
dirty = 300240 + 135280 = 435520
ratio = 435520 / 3583312 = 0.122...
我发现有一件事限制了它,但还不足以看到这些结果。我一直在尝试设置/sys/class/bdi/*max_ratio
。问题中的测试结果来自 max_ratio = 1 的运行。
使用 重复上述测试max_ratio = 100
,我可以获得更高的脏率,例如 0.142:
MemFree: 122936 kB Cached: 3012244 kB Dirty: 333224 kB Writeback: 13532 kB
写入测试需要相当长的时间才能可靠地观察到这一点,例如 8GB。此测试大约需要 100 秒。我正在使用旋转硬盘。
我尝试使用 4GB 进行测试,只看到脏率为 0.129:
MemFree: 118388 kB Cached: 2982720 kB Dirty: 249020 kB Writeback: 151556 kB
正如我所说,这让我感到惊讶。我有一个专家来源2013年,说dd
应该“自由运行”来生成脏页,直到系统达到脏率 0.15。它明确地谈论max_ratio
.
答案1
“比率”是指脏页缓存(又名回写缓存)的大小,以
MemFree
+的百分比表示Cached
。它们不是 MemTotal 的百分比 - 这就是我在上述问题中感到困惑的地方。
不,这个描述仍然不准确。
Cached
包括tmpfs
、 和其他Shmem
分配中的所有文件。它们被计数是因为它们是使用页面缓存实现的。然而,它们不是任何持久存储的缓存。它们不能就这样被丢弃。 tmpfs
页面可以交换,但可交换的页面不包含在计算中。
我有 500-600MB 的Shmem
.当我尝试再次查看跟踪点时(请参阅上一个问题的答案),这大约是正确的数量,可以解释为什么limit
/ 0.20 低于我的预期。
还Cached
排除Buffers
, 它可以是在某些设置上数量惊人地大。
我想我应该仔细看看实施global_dirtyable_pages()对于我的内核版本,并使用/proc/vmstat
.或者也许专注于使用跟踪点。
答案2
在 ext2/4 上,有一个基本的每 5 秒刷新默认值data=ordered
,这也是默认值:
commit=nrsec
Start a journal commit every nrsec seconds. The default value is 5 seconds. Zero means de-
fault.
因此,如果您的页面只是不想保持脏状态(或保持脏状态太久),那么这是第一个需要调整的地方。
这里有一个困境,几年前 ext4 丢失数据就出现了这个大问题(请参阅noauto_da_alloc安装选项):
内核仅在必要时才准备好写入 - 这就是为什么您可以通过大小和年龄甚至通过限制来限制。 VM充当主存储的缓存。
“桌面”用户大多还没有准备好接受这个附加层。当他节省一个文件,他排除它之后立即物理存在于存储中。这就是为什么vim
有 fs 或 fsync 选项:
启用后,将在写入文件后调用库函数 fsync()。这会将文件刷新到磁盘,确保即使在仅进行元数据日志记录的文件系统上也可以安全地写入该文件。这将强制硬盘驱动器在以笔记本电脑模式运行的 Linux 系统上旋转,因此在某些情况下这可能是不可取的。
我有 500'000 个来自 git repo 操作的脏页;然后它达到了background
尺寸极限并完全消失了。当 git 决定重新打包/垃圾收集时,它似乎也会同步,有点像vim
set fs
。
(任何其他分区的)挂载也会进行同步。
但是,由于 ext4 日志提交时间长ratio
、长expire_centisecs
、没有 5 秒,而且没有人(vim、git)同步,一半的 RAM 可能会脏几个小时。
答案3
我缩小了 dd 的大小,只写几页:
dd if=/dev/zero of=zerooo count=250 bs=4096
该脚本显示了重要参数:
grep dirty /proc/vmstat
grep '' /proc/sys/vm/*centi*
grep '' /proc/sys/vm/dirt*ratio
过期实际上已关闭,并且阈值高于 250 dd 页:
nr_dirty 0
nr_dirty_threshold 2398
nr_dirty_background_threshold 1199
/proc/sys/vm/dirty_expire_centisecs:360000
/proc/sys/vm/dirty_writeback_centisecs:360000
/proc/sys/vm/dirty_background_ratio:0
/proc/sys/vm/dirty_ratio:0
比率为 0,表明我回显了绝对参数:
/proc/sys/vm/dirty_background_bytes:9819200
/proc/sys/vm/dirty_bytes:9819200
前导 9 将 819200 字节 = 200 页转换为约 1000 页,请参见上面的“阈值”。
现在安装选项很重要;不幸的是,我还安装了“data=writeback”,我不知道这是否间接阻止了一些刷新。但是 noauto、long commit 和“writeback”似乎都是 dd 只生成直接脏页所必需的。
TARGET SOURCE FSTYPE OPTIONS
/ /dev/sda3 ext4 rw,relatime,noauto_da_alloc,data=writeback
`-/mnt/sda/4 /dev/sda4 ext4 rw,relatime,noauto_da_alloc,commit=3600,data=writeback
现在:
$ time dd if=/dev/zero of=zerooo count=150 bs=4096
150+0 records in
150+0 records out
614400 bytes (614 kB, 600 KiB) copied, 0.00106973 s, 574 MB/s
real 0m0.003s
user 0m0.000s
sys 0m0.003s
还有原版。并行运行的脚本:
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 600 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback:
下降到零就是我所做的 rm Zeroooo。过期时间很长,它会一直存在。
后:
echo 25600 > /proc/sys/vm/dirty_bytes
门槛很小;背景值自动调整为 50%!:
nr_dirty 0
nr_dirty_threshold 7
nr_dirty_background_threshold 3
现在我得到:
$ time dd if=/dev/zero of=zerooo count=150 bs=4096
150+0 records in
150+0 records out
614400 bytes (614 kB, 600 KiB) copied, 0.0258571 s, 23.8 MB/s
real 0m0.028s
user 0m0.003s
sys 0m0.000s
更久,更长!节流!并且只有一些残留的 12 kB 脏数据:
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 12 kB Writeback: 0 kB
Dirty: 12 kB Writeback: 0 kB
Dirty: 12 kB Writeback: 0 kB
Dirty: 12 kB Writeback: 0 kB
... snip
Dirty: 12 kB Writeback: 0 kB
Dirty: 12 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
rm Zerooo 再次将 nr_dirty 设置为零。
DD#3:
上限远高于 150 页,但背景仍为 3:
nr_dirty 0
nr_dirty_threshold 2448
nr_dirty_background_threshold 3
dd 全速回来了。 dd 并不关心这些页面到底在哪里——它们已经被写入了。
$ time dd if=/dev/zero of=zerooo count=150 bs=4096
150+0 records in
150+0 records out
614400 bytes (614 kB, 600 KiB) copied, 0.0011005 s, 558 MB/s
real 0m0.003s
user 0m0.003s
sys 0m0.000s
结果与第二次运行类似:一些 kB 未被“背景”刷新,但这可能只是一些人为的分数,因为数字较低。根据我的经验,当它启动时,它会刷新到“零”。
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 36 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
Dirty: 0 kB Writeback: 0 kB
现在只需更改:
mount -o remount,commit=13 /mnt/sda/4
背景超过 150 页(参见第一次运行)时,即使使用 data=writeback,我也会在 13 秒后得到额外的脏信息:
Dirty: 0 kB
Dirty: 0 kB
Dirty: 0 kB
Dirty: 456 kB ??? seems to appear always
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 600 kB
Dirty: 620 kB !!! after 13 seconds commit= mount option
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB
Dirty: 620 kB rm zeroooo
Dirty: 20 kB
Dirty: 20 kB
Dirty: 20 kB
Dirty: 20 kB
Dirty: 20 kB
Dirty: 20 kB sync
Dirty: 0 kB
这个 5 秒的数据和元数据包应该由 /proc/sys/vm/ 中的“centsecs”参数每 30 秒刷新一次。但只能在一起。这就是data=ordered
发挥作用的地方。
nr_dirty
标题:当然,如果您按 ctrl-c dd 然后启动监视脚本,那么 /proc/vmstat 中的内容不能超过阈值上限。