该文章参考宋宝华老师的内存管理课程,详细可以去听阅码场宋老师的课程。
● DMA和cache一致性
● 内存的cgroup
● 性能方面的调优:page in/out, swapin/out
● Dirty 页面的写回,Dirty ratio的一些设置
● 内存的回收-水位的设置
● swappiness
为什么内存的大小 会严重影响你系统的性能?
因为 CPU 操作IO 都是通过 内存来实现的,内存大小直接关系到 IO的速度。
内存大 page cache 大,程序切换时,页表切换就不用挤出匿名页来做交换。程序切换就很流畅。
dirty data 什么时候写回,内存什么时候回收,这些都会影响到系统的性能
DAM 和 cache 的一致性
请参考下面两篇详细的文章 :
DMA内存大小、连续性、cache一致性、如何配置CMA的总结
cgroup 的使用案例
QoS 基于服务质量的优化
(1) 创建一个 cgroup 的 memory group ,可以把自己的多个进程加到这个组里面。
(2) 限制 这个 memory 组A的进程最大可用的内存就是 200M ,如果超过200M 就会被killed
写一个申请2000M 大小内存的程序
apt install cgroup-tools
如果当内存用完之后,不想被kill掉,可以用下面的参数 : 参考: Documentation/cgroup-v1/memory.txt
这些 cgroup 可以帮你做到一些QoS ,也就是基于服务质量的控制,内存大的group就更容易命中,服务质量就好。
QoS 一般让qq 性能好的同时,是牺牲word性能换取的。类似于机场头等舱、商务舱的旅客先登场,经济舱的排队最后登场。就是分了一个group
dirty page 的写回
之前我们了解过,CPU 读写磁盘,其实都是通过 memory 来实现的,应用程序其实是感受不到磁盘的存在的,读写的本质是读写的 page cache ,这些page cache 在没有被写入 disk 之前,都叫做 dirty page ,
关于 dirty page 什么时候写回,写到磁盘,其实有些参数是可以配置的
我画了下面的图,从时间和空间的两个维度来理解 dirty page 何时被写回
时间维度:
时间维度:
dirty_expire_centisecs
告诉 kernel flush threads 多长时间(1s/100 为单位),就认为 dirty page is old enough ,
已经 ready to flush了,达到这个时间,并不会立刻将 dirty page 写到disk ,
而是要等到 下次 kernel flush threads 唤醒时,才会写回。
dirty_writeback_centisecs
kernel flush threads 多长时间(1s/100 为单位) wake up 一次,来 flush dirty page old enough
看了一下 x64平台 和 ARM32 平台的时间设置的是30s 和 5s
# cat /proc/sys/vm/dirty_expire_centisecs
3000
# cat /proc/sys/vm/dirty_writeback_centisecs
500
也就是每5s kernel flush threads 就会wake up 一次,把内存中已经存在30s或者30s以上的时间的 dirty 写回到 disk
空间维度:
首先 内存当中 肯定不能有太多的 dirty page ,因为如果内存中全是 dirty page ,其他人要申请内存,肯定也要讲 dirtypage 写回的,才能交换出来内存,这样会影响性能,所以 dirty page 在内存中肯定有一个空间的限制的。
假设这两个参数为 dirty_background_ratio=10 (内存大小的10%)dirty_ratio=20 (内存大小的20%)
由于写内存的速度要远大于写IO的速度。所以当你应用程序 狂写硬盘的时候,写到10%内存的时候,其实应用程序只是在写内存,并没有感受到disk的存在。但是现在你达到 dirty_background_ratio(10%)
了,这个时候 kernel flush threads 就会帮你把 dirty page 写到disk ,如果你现在的应用程序停了,那么你的应用程序就从未感受到 磁盘的存在,
如果你的应用程序继续狂写,dirty page 达到了 20% ,不好意思,后台flush的速度,赶不上你前台 写内存的速度,那么这个时候,Linux就会直接堵住你的前台进程,它要保证内存中的 dirty page 不超过 内存的20%
一般情况下,比如们时不时的写一下,操作一下word 什么的,可能根本就达不到 dirty_background_ratio,这个时候真正起作用的其实是时间维度的那个参数,但是如果你剧烈的写磁盘,写到10% - 20% 之间的时候,你同样感受不到硬盘的存在,本质就是在读写内存。只有你写到20%的时候,你的应用程序被阻塞的时候,你才能感受到硬盘的存在。
所以像视频 音频输出到 磁盘的时候,存1080p的图片,要注意,写过一段时间之后,可能出现卡顿的现象。
内存回收的门限?
内存回收和脏页写回是两个概念,脏页写回了,写到disk了,但是 page cache 还在啊,APP下回再读的时候,这个page还是在的,并没有被回收。
内存回收是将内存中的page 都丢弃了,下次再用的时候,从disk中再都回来。
关于内存回收的故事。
内存回收的对象是谁?
可能是 file-backed page , 也可能是 swap ,到底先回收哪一个呢,天平应该往那一边倾斜呢,这个在linux 里面是通过 swapiness这个参数来设置的
内存回收的参数-- swapiness : 反应是否积极的使用swap 空间
swapiness 越大,越倾向于回收匿名页,swapiness 越小,越倾向于回收文件背景的页面。
是不是将 swapiness = 0 ,匿名页是不是 就不交换了呢?有一种特殊情况,当你系统的 free + file-backed pages 加起来都不足 高水位的内存,
也就是把系统中的所有 文件背景的页面都回收了,也达不到高水位,这个时候还是需要交换匿名页的(先交换,再回收)。
但是有个例外,就是对于 cgroup 而言,当你把 cgroup 中的 这个参数设为0 ,就不会再交换 匿名页了。
为什么一定要保证 min 水位呢,这个 min_free_kbytes ?
因为 Linux 里面是有一些情况是要使用 紧急内存的,比如 回收内存的代码,它也是需要内存的啊,它在申请内存的时候,会加上一个 PF_MEMALLOC 的标志,
带上这个标志之后,就可以不收 min 水位的限制,使用min水位一下的内存。
这就像一个军队快要饿死了,要去增粮草,但是这个增粮队得吃饭吧,不能饿死啊,军队没有粮食了,但是最后那一点点粮食,要保证增粮队得有的吃,非增粮队的人都不能吃,才能拯救这个军队。
min_free_kbytes 是根据你的物理内存的大小而变化的,随着你的 lowmem的增大而增大,但是并不是线性的,每个zone的计算方法不一样。上图中说明了一种。
当然你也可以修改 lowmem_reserve_ratio 来修改 min_free_kbytes 的值。
怎么修改呢??
# cat /proc/sys/vm/lowmem_reserve_ratio
256 256 32 1
cat /proc/zoneinfo
还有一个经常被改的参数,vfs_cache_pressure 表示 内核回收 icache 和 dcache的倾向。默认是100,
大于100,越大越倾向于回收 icache 和 dcache
Linux 会经常读文件,就会在 系统里 创建一些 可以回收的 内存 slab,,系统读过的文件的 inode 和 dentry 的信息都存到page cache(icache 和 dcache) , 注意这个cache 和 指令数据cache是不一样的,
这个就是内存,可以回收的。
手动回收,清除 pagecache可以通过下面的命令:
getdelay 的工具的使用,这个工具又小巧,又好用。
Documentation/accounting/delay-accounting.txt 有使用方法。
比如系统内存1G,程序里申请1G,写的时候会 swap out , 读的时候会 swap in
vmstat
可以看出来 里面有很多的 swap in / swap out , 伴随着 磁盘IO bi bo
找一个带 timer 的函数可以用 apropos 来看,然后在用man