[TOC] ## 其他工程问题以及调优 * DMA和cache一致性 * 内存的cgroup * memcg子系统分析 * 性能方面的调优: page in/out, swap in/out * Dirty ratio的一些设置 * swappiness ### DMA和cache一致性 ![dma_cache一致性.jpeg](https://box.kancloud.cn/38e774e8a00856261b488b738dd3a7a5_1468x1066.jpeg) 工程中,DMA可以直接在内存和外设进行数据搬移,而CPU访问内存时要经过MMU。DMA访问不到CPU内部的cache,所以会出现cache不一致的问题。因为CPU读写内存时,如果在cache中命中,就不会再访问内存。 当CPU 写memory时,cache有两种算法:write_back ,write_through。一般都采用write_back。cache的硬件,使用LRU算法,把cache中的数据替换到磁盘。 ![dma_api.png](https://box.kancloud.cn/45e902583a15771d865f370665ffa14c_979x579.png) cache一致性问题,主要靠以上两类api来解决这个问题。一致性DMA缓冲区api,和流式DMA映射api。CPU通过MMU访问DMA区域,在页表项中可以配置这片区域是否带cache。 现代的SoC,DMA引擎可以自动维护cache的同步。 ### 内存的cgroup 进程分group,内存也分group。 进程调度时,把一组进程加到一个cgroup,控制这一组进程的CPU权重和最大CPU占用率。在/sys/fs/cgroup/memory创建一个目录,把进程放到这个group。可以限制某个group下的进程不用swap,每个group的swapiness都可以配置。 比如,当你把某个group下的swapiness设置为0,那么这个group下进程的匿名页就不允许交换了。 /proc/sys/vm/swapiness是控制全局的swap特性,不影响加到group中的进程。 也可以控制每个group的最大内存消耗为200M,当这个group下进程使用的内存达到200M,就oom。 demo: 演示用memory cgroup来限制进程group内存资源消耗的方法 ``` swapoff -a echo 1 > /proc/sys/vm/overcommit_memory # 进程申请多少资源,内核都允许 root@whale:/sys/fs/cgroup/memory# mkdir A root@whale:/sys/fs/cgroup/memory# cd A root@whale:/sys/fs/cgroup/memory/A# echo $((200*1024*1024)) > memory.limit_in_bytes cgexec -g memory:A ./a.out [ 560.618666] Memory cgroup out of memory: Kill process 5062 (a.out) score 977 or sacrifice child [ 560.618817] Killed process 5062 (a.out) total-vm:2052084kB, anon-rss:204636kB, file-rss:1240kB ``` ### memory cgroup子系统分析 memcg v1的参数有25个, 通过数据结构 res_counter 来计算。 ``` ~~~ /* * The core object. the cgroup that wishes to account for some * resource may include this counter into its structures and use * the helpers described beyond */ struct res_counter { unsigned long long usage; /* * 目前资源消费的级别 */ unsigned long long max_usage; /* *从counter创建的最大使用值 */ unsigned long long limit; /* * 不能超过的使用限制 */ unsigned long long soft_limit; /* * 可以超过使用的限制 */ unsigned long long failcnt; /* * 尝试消费资源的失败数 */ spinlock_t lock; /* * the lock to protect all of the above. * the routines below consider this to be IRQ-safe */ struct res_counter *parent; /* * Parent counter, used for hierarchial resource accounting */ }; ``` 内存的使用量 mem_cgroup_usage 通过递归RSS和page cache之和来计算。 `struct mem_cgroup`是负责内存 cgroup 的结构 ~~~ struct mem_cgroup { struct cgroup_subsys_state css; // 通过css关联cgroup. struct res_counter res; // mem统计变量 res_counter memsw; // mem+sw的和 struct res_counter kmem; // 内核内存统计量 ... } ~~~ 这些参数的入口都在`mm/memcontrol.c`下,比如说`memory.usage_in_bytes`的读取调用的是`mem_cgroup_read`函数, 统计的入口是`mem_cgroup_charge_common()`,如果统计值超过限制就会在cgroup内进行回收。调用者分别是缺页时调用的`mem_cgroup_newpage_charge`和 page cache相关的`mem_cgroup_cache_charge`。 当进程进入缺页异常的时候就会分配具体的物理内存,当物理内存使用超过高水平线以后,换页daemon(kswapd)就会被唤醒用于把内存交换到交换空间以腾出内存,当内存恢复至高水平线以后换页daemon进入睡眠。 缺页异常的入口是 `__do_fault`, `RSS`在`page_fault`的时候记录,`page cache`是插入到`inode`的`radix-tree`中才记录的。 `RSS`在完全`unmap`的时候减少计数,`page cache`的`page`在离开`inode`的`radix-tree`才减少计数。 即使`RSS`完全unmap,也就是被`kswapd`给换出,可能作为SwapCache存留在系统中,除非不作为SwapCache,不然还是会被计数。 一个换入的`page`不会马上计数,只有被`map`的时候才会,当进行换页的时候,会预读一些不属于当前进程的`page`,而不是通过`page fault`,所以不在换入的时候计数。 参考文档: https://ggaaooppeenngg.github.io/zh-CN/2017/05/07/cgroups-%E5%88%86%E6%9E%90%E4%B9%8B%E5%86%85%E5%AD%98%E5%92%8CCPU/ ### 脏页写回的“时空”控制 ![dirtypage_writeback.png](https://box.kancloud.cn/ad16eeb3f67408a02feb972edd5551b5_984x619.png) “脏页”:当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是一致的。 通过时间(dirty_expire_centisecs)和比例,控制Linux脏页返回。 dirty_expire_centisecs:当Linux中脏页的时间到达dirty_expire_centisecs,无论脏页的数量多少,必须立即写回。通过在后台启动进程,进行脏页写回。 默认时间设置为30s。 dirty_ratio,dirty_background_ratio 基于空间的脏页写回控制。 不能让内存中存在太多空间的脏页。如果一个进程在循环调用write,当达到dirty_background_ratio后,后台进程就开始写回脏页。默认值5%。当达到第2个阈值dirty_ratio时,应用进程被阻塞。当内存中的脏页在两个阈值之间时,应用程序是不会阻塞。 ### 内存何时回收:水位控制 脏页写回不是 内存回收。 脏页写回:是保证在内存不在磁盘的数据不要太多。 水位控制:是指内存何时开始回收。 ![水位控制](https://box.kancloud.cn/d5788f17d84ce78bb918db959b4fae2f_1007x641.png) 由/pro/sys/vm/min_free_kbytes 控制,根据内存大小算出来的平方根。 pf_mem_alloc,允许内存达到低水位以下,还可以继续申请。内存的回收,在最低水位以上就开始回收。 ![水位](https://box.kancloud.cn/dee381a5f0d81a0ddb17e5af0facd0ab_1073x686.png) 每个Zone都有自己的三个水位,最小的水位是根据min_free_kbytes控制。5/4*min_free_kbytes =low 3/2*min_free_kbytes =high , Zone的最小内存达到5/4的low 水位,Linux开始后台回收内存。直到达到6/4的high水位,开始不回收。 当Zone的最小内存达到min水位,应用程序的写会直接阻塞。 实时操作系统, ![swapiness.png](https://box.kancloud.cn/902928df0f37032af7423b6ae2ceb1d7_886x636.png) 当你要开始回收内存时,回收比例通过swappiness越大,越倾向于回收匿名页;swappiness越小,越倾向于回收file-backed的页面。 当把cgroup中的swapiness设置为0,就不回收匿名页了。 当你的应用会经常去访问数据malloc的内存,需要把swapiness设置小。dirty的设置,水位的设置都没有一个标准,要看应用使用内存的情况而定。 getdelays工具:用来评估应用等待CPU,内存,IO,的时间。 linux/Documents/accounting ![getdelays.png](https://box.kancloud.cn/0d0a0b6006c41451aee688dab8bb580a_974x651.png) CONFIG_TASK_DELAY_ACCT=y CONFIG_TASKSTATS=y vmstat 可以展现给定时间间隔的服务器的状态值,包括Linux的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。 ``` vmstat 1 ``` Documents/sysctl/vm.txt 中有所有参数最细节的描述。