关于Meminfo中MemAvailable 理解

我们知道linux提供了proc文件系统可以给用户查看一些系统信息,其中与内存相关的文件包含如下:

  • /proc/meminfo
  • /proc/zoneinfo
  • /proc/vmstat
  • /proc/buddyinfo
  • /proc/sys/vm/

上述这些内容就是我们在遇到一些压测类问题时常看的内容,本部分内容介绍对于meminfo中的MemAvailable字段的理解;

本文主要介绍如下内容:

  1. MemAvailable计算过程中相关的概念理解说明,包含managed_pages、min_free_kbytes、watermark等
  2. MemAvailable的计算过程;
  3. 系统提供了哪些文件接口可以调整其大小?

1. MemAvailable 的用处

首先来看meminfo中的内容:

我们主要会关注三个主要指标:total、free和available:

  • MemTotal 用于描述受kernel管理的所有内存项;
  • MemFree 用于描述系统中处于Free状态的内存项;
  • MemAvailable 用于描述系统中可供分配的内存项,注意这一项是一个估测值,其中包含Free和可回收的一部分内存项;

一般来说我们认为available项还有空间的话说明系统还处于一个正常状态,那么Avialable这一项是怎样算出来的呢?

2. MemAvailable的计算

我们先回答第一个问题,即Available这一项是如何计算出来的,直接上code:

available = si_mem_available(); //计算获取到available
...
show_val_kb(m, "MemAvailable:   ", available);     //将其显示出来//具体来看 si_mem_available()的实现:/mm/page_alloc.c
long si_mem_available(void)
{long available;unsigned long pagecache;unsigned long wmark_low = 0;unsigned long pages[NR_LRU_LISTS];struct zone *zone;int lru;for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)pages[lru] = global_node_page_state(NR_LRU_BASE + lru);for_each_zone(zone)wmark_low += zone->watermark[WMARK_LOW];//遍历zonelist获取每个zone的watermark值available = global_page_state(NR_FREE_PAGES) - totalreserve_pages;//free - reserve//注意这里reserve指的是各个zone中的lowmem部分和watermark[high]部分//接下来计算pagecache中可回收的部分,这里回收FILE部分而未计算anoy部分是基于处理效率的考虑,anoy部分需要swap申请buffer以及emmc空间写入,而file只需要discard或者writeback即可;pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];pagecache -= min(pagecache / 2, wmark_low);//取一半和wmark_low中较小的那个去除;available += pagecache;//接下来计算slab分配器分配内容中可回收的部分available += global_page_state(NR_SLAB_RECLAIMABLE) - min(global_page_state(NR_SLAB_RECLAIMABLE) / 2, wmark_low);if (available < 0)available = 0;return available;
}

上述code中的注释和图示已经将上述逻辑做了说明,即available是free的内存项中去除维持系统运行所必须的部分再加上可回收部分;

计算过程中实际引入了totalreserve_pages、watermark等几个字段,接下来依次介绍其含义:

2.1 totalreserve_pages 理解

上述计算中,我们首先用free的内存项去除了reserve的部分,这个reserve部分表示系统运行所必须的部分,主要包含lowmem和watermark部分:

整体计算即如上述图示说明,即各个zone中的managed / ratio 累加后再叠加上watermark所必备的部分即reserve部分;

//totalreserve_pages定义在:/include/linux/mmzone.h 是pglist_data的成员
/** calculate_totalreserve_pages - called when sysctl_lowmem_reserve_ratio* or min_free_kbytes changes.*/
static void calculate_totalreserve_pages(void)
{struct pglist_data *pgdat;unsigned long reserve_pages = 0;enum zone_type i, j;for_each_online_pgdat(pgdat) {//对所有的node进行遍历,嵌入式设备只有一个nodepgdat->totalreserve_pages = 0;for (i = 0; i < MAX_NR_ZONES; i++) {//对所有zone进行遍历,如无特别处理则有:DMA \ Normal \ HighMem 三个zonestruct zone *zone = pgdat->node_zones + i;long max = 0;for (j = i; j < MAX_NR_ZONES; j++) {//取zone中lowmem_reserve最大的那个if (zone->lowmem_reserve[j] > max)max = zone->lowmem_reserve[j];}max += high_wmark_pages(zone);if (max > zone->managed_pages)//判断如果max数值超过了kernel对应zone中管理的pages,则标记为managed_pagesmax = zone->managed_pages;//如无bug,这里应该很难进的来;pgdat->totalreserve_pages += max;reserve_pages += max;//对于我们的板子来说,这两个值是一样的;}}totalreserve_pages = reserve_pages;
}
//经过上述计算过程,lowmem_reserve和managed_pages这两个变量需要确认其计算过程
/** setup_per_zone_lowmem_reserve - called whenever*    sysctl_lowmem_reserve_ratio changes.  Ensures that each zone*   has a correct pages reserved value, so an adequate number of*   pages are left in the zone after a successful __alloc_pages().*/
static void setup_per_zone_lowmem_reserve(void)
{struct pglist_data *pgdat;enum zone_type j, idx;for_each_online_pgdat(pgdat) {//遍历所有nodefor (j = 0; j < MAX_NR_ZONES; j++) {//遍历所有zone,都是一样的套路,目的就是计算出来每个zone的数值struct zone *zone = pgdat->node_zones + j;unsigned long managed_pages = zone->managed_pages;//从zone中获取到managed_pageszone->lowmem_reserve[j] = 0;idx = j;while (idx) {struct zone *lower_zone;idx--;if (sysctl_lowmem_reserve_ratio[idx] < 1)//获取到这个系数,默认配置为256 32 32sysctl_lowmem_reserve_ratio[idx] = 1;lower_zone = pgdat->node_zones + idx;lower_zone->lowmem_reserve[j] = managed_pages / sysctl_lowmem_reserve_ratio[idx];//managed / 系数managed_pages += lower_zone->managed_pages;}}}/* update totalreserve_pages */calculate_totalreserve_pages();
//    free_area_init_core 接口中描述了managed_page的初始化过程,zoneinfo中可以看到其数值,zone结构体中可以看到其定义
//这里没有将managed_pages计算的部分列出来,后续有机会再补充
}

2.2 watermark 理解

直接来看图示:

watermark的计算相较于reservedmem来说要复杂一些,主要包含:

  1. 计算min_free_kbytes,此数据是根据managed_pages计算出来的,可以通过proc文件系统修改配置;
  2. 计算watermark,简单来说就是将min_free_kbyte按照比例划分给各个zone,就是watermark[WMRK_MIN];

详细来看code:

static void __setup_per_zone_wmarks(void)
{unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);//将min_free_kbytes转换为pagesunsigned long lowmem_pages = 0;struct zone *zone;unsigned long flags;for_each_zone(zone) {if (!is_highmem(zone))lowmem_pages += zone->managed_pages;//计算出所有lowmem_pages有多少;}for_each_zone(zone) {u64 tmp;spin_lock_irqsave(&zone->lock, flags);tmp = (u64)pages_min * zone->managed_pages;do_div(tmp, lowmem_pages);//这两步计算就是按照比例划分lowmem而已if (is_highmem(zone)) {//对于highzone部分有些差别,他不是lowmem,所以这里计算出来的是额外地;unsigned long min_pages;min_pages = zone->managed_pages / 1024; //处理也是很简单,直接 / 1024min_pages = clamp(min_pages, SWAP_CLUSTER_MAX, 128UL);//确保在范围内,128~64M吧zone->watermark[WMARK_MIN] = min_pages;} else {zone->watermark[WMARK_MIN] = tmp;}tmp = max_t(u64, tmp >> 2, mult_frac(zone->managed_pages, watermark_scale_factor, 10000));//这里是做个比较,tmp /4 和 managed_page * factor / 10000的大小,默认情况下这里是/1000,tmp较大;zone->watermark[WMARK_LOW]  = min_wmark_pages(zone) + tmp;zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2;spin_unlock_irqrestore(&zone->lock, flags);}calculate_totalreserve_pages();
}/** Initialise min_free_kbytes.** For small machines we want it small (128k min).  For large machines* we want it large (64MB max).  But it is not linear, because network* bandwidth does not increase linearly with machine size.  We use**  min_free_kbytes = 4 * sqrt(lowmem_kbytes), for better accuracy:*   min_free_kbytes = sqrt(lowmem_kbytes * 16)** which yields** 16MB:  512k* 32MB: 724k* 64MB: 1024k* 128MB:   1448k* 256MB:   2048k* 512MB:   2896k* 1024MB:  4096k* 2048MB:  5792k* 4096MB:  8192k* 8192MB:  11584k* 16384MB:    16384k*/
int __meminit init_per_zone_wmark_min(void)
{unsigned long lowmem_kbytes;int new_min_free_kbytes;lowmem_kbytes = nr_free_buffer_pages() * (PAGE_SIZE >> 10);//将获取到的page转换为kbytesnew_min_free_kbytes = int_sqrt(lowmem_kbytes * 16);//这个计算就很简单了,上述注释也有说明;if (new_min_free_kbytes > user_min_free_kbytes) {min_free_kbytes = new_min_free_kbytes;if (min_free_kbytes < 128)//最小128kbytesmin_free_kbytes = 128;if (min_free_kbytes > 65536)min_free_kbytes = 65536;//最大65536Kbytes} else {pr_warn("min_free_kbytes is not updated to %d because user defined value %d is preferred\n",new_min_free_kbytes, user_min_free_kbytes);}setup_per_zone_wmarks();refresh_zone_stat_thresholds();setup_per_zone_lowmem_reserve();return 0;
}
core_initcall(init_per_zone_wmark_min)

整个计算的过程还是蛮简单的,理解起来也不复杂;

另外说明下这个watermark是用来干啥的:

字段 说明
zone->watermark[WMARK_MIN] 低于min说明内存已经非常紧张了,需要kswap线程持续回收内存,并且在申请时会直接释放后分配
zone->watermark[WMARK_LOW] 低于low说明此时内存已经比较紧张,需要唤醒swap进行释放直至到达high
zone->watermark[WMARK_HIGH] 高于high说明此时内存比较健康

3. 如何改变MemAvailable的值

根据前文的计算我们可以知道,MemAvailable表示系统中包含可回收的内存总共有多少可以被分配的Mem;

其依赖于reserveMem 和 watermark:

  1. 修改 sysctl_lowmem_reserve_ratio 可以改变reservemem的大小,即减小系数可以增加reserve;
  2. 修改 min_free_kbyte可以改变watermark的大小;
  3. 修改 watermark_scale_factor会改变watermark[low & high]的大小,进而影响到reserve的大小;

通过修改上述即可字段即可以改变到计算出来的available的大小,不过貌似也只是改变了触发回收mem的时机,没有太多的作用的样子;

4. meminfo中其他字段

5. 附录

相关涉及文件目录:

目录 说明
/fs/proc/meminfo.c meminfo的入口
/mm/page_alloc.c si_mem_available等相关计算实现文件
/include/linux/mmzone.h totalreserve_pages定义

这几天跟踪了这部分code,顺便将其简单整理了出来,写的没什么逻辑

关于Meminfo中MemAvailable 理解相关推荐

  1. 在实践中深入理解IP协议

    本文为我个人计划撰写的博客专题<在实践中深入理解常见网络协议>中关于IP协议的一篇,有兴趣的朋友可以继续关注我的博客,我将会陆续撰写各种协议的实践分析文章. TCP/IP协议栈其实当然不止 ...

  2. 博客专题计划:《在实践中深入理解常见网络协议》

    为什么要写这个系列的技术博文: 距离学习CCIE的课程已经有近一年的时间,虽然这一年来已经丢下了挺多关于路由交换技术的知识,不过随着这一年时间以来通过对Linux和Python的学习研究和学校相关课程 ...

  3. 干货!任务型对话中语言理解的鲁棒性测试 |清华刘劼西

    点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 本期AI TIME PhD直播间,我们邀请到了清华大学计算机科学与技术系的硕士生--刘劼西,带来分享--<任务型对话中语言理解的鲁 ...

  4. IOC在墨者革离中的理解

    IOC在墨者革离中的理解 实现墨者革离场景 首先是实现MoAttack public class MoAttack{public void cityGateAsk(){//让演员进场LiuDeHua ...

  5. Android 设计模式之MVC,从一个实例中来理解MVC

    前言 已经有几天没有写过帖子了,主要前一段时间在忙公司的项目,现在闲下来想看一下其他的东西,然后从公司的iOS大神那里了解到了苹果的设计模式是MVC,于是想着自己也来写一下Android这边的MVC. ...

  6. 智能催收系统中自然语言理解模块设计

    摘要 随着社会信贷消费的流行,待催收的违约用户逐渐增多.针对此问题,对Rasa对话系统开发框架中的自然语言理解模块进行改进,显著提升智能催收系统中自然语言理解模块的准确率,并对最终得到的训练模型进行性 ...

  7. 体素坐标(voxel_coors)在mmdetection3d中的理解

    体素坐标(voxel_coors)在mmdetection3d中的理解 针对KITTI数据集举例. 1. 范围说明 point_cloud_range [0, - 40, -3, 70.4, 40, ...

  8. 在python中、对于函数定义代码的理解_python中如何理解装饰器代码?

    长文预警,[最浅显易懂的装饰器讲解] 能不能专业地复制题目?配上代码,问题分段. 我来给提主配上问题的代码. 正式回答: 1:如何理解return一个函数,它与return一个值得用法区别在哪? 敲黑 ...

  9. ARP-地址解析协议(在实践中深入理解ARP协议)

    在同一个网络(无特别说明,均指以太网络)中进行通信的主机,必须要拥有目标主机的MAC地址才能够正确地将数据发送给目标主机,那么如何知道目标主机的MAC地址呢?可以通过ARP协议.ARP协议就是用来获取 ...

  10. oracle中@,深入理解Oracle中的DBCA

    但凡是学习 过Oracle的同学,DBCA都是一个必备工具,有了这个工具,创建数据库成为可能.而DBCA本身有图形和静默两种方式.静默方式看起来高大上,可以轻松搞定一个看似很复杂的创建数据库过程,而只 ...

最新文章

  1. 【tensorflow】OP_REQUIRES failed at variable_ops.cc:104 Already exists: Resource
  2. 文本比较算法Ⅴ——回顾贴,对前面几篇文章的回顾与质疑
  3. Jzoj4209 已经没有什么好害怕的了
  4. 实现tree系统命令
  5. extjs设计器破解程序及开发调试工具
  6. 设计模式之-简单工厂模式
  7. 1027. 打印沙漏(20)-PAT乙级真题
  8. java exception 包_什么是Java中的异常包装?
  9. HDMI 分配器正确使用方法
  10. 广义速度V与管理理论——流水线、TPS…
  11. 吃桃子削不削皮 如何吃有保证
  12. 数学模型 Lotka-Volterra
  13. 有没有java自编歌曲_简易音乐播放器制作
  14. 如何让iPad浏览器不再拒绝访问请求
  15. 蓝牙热敏打印开发(佳博打印机)
  16. PL/SQL教程:PL/SQL Developer使用技巧
  17. “北美之鹰”试图借助Windows 10物联网核心版打破世界陆地速度记录
  18. 【转】张飞眼中的真实三国-爆笑日记
  19. React路由跳转时通过传参进行动态渲染的方法
  20. Linux expr 命令详解

热门文章

  1. webpack@3.6.0(4) -- 配置模块化开发
  2. Thread.Join 和 Task.Wait 方法
  3. Coded UI Test学习网站
  4. 【Struts1】--beanutils
  5. 使用IronPython集成Python和.NET
  6. 【玩转Ubuntu】02. Ubuntu上搭建Android开发环境
  7. 如何将SL的image保存到SL的独立存储文件系统
  8. matlab实现epirb调制,[单选] 根据《建筑安装工程费用项目组成》(建标[2003]206号)的规定,下列属于直接工程费中材料费的是()。...
  9. centos7 安装mysql_第02期:ClickHouse 单机部署以及从 MySQL 增量同步数据
  10. Java-web下使用RSA进行加密解密操作