Linux关于memory cgroup的几个要点

概述

本文讲述memory cgroup比较容易误解的一些逻辑,如果不太经常使用和解决问题的话,对于memory cgroup的认知会比较浅显:cgroup memory用来限制进程的内存使用,但是我们进一步想如下的问题:

  1. 进程的内存可以分很多类型,比如page cache,slab,anon memory等,到底是限制的哪些内存?
  2. 如果进程A已经运行起来占用了一些内存,之后,再将A加入memory cgroup限制,原来占用的内存会统计入新的memory cgroup?
  3. memory cgroup有memory.soft_limit_in_bytes和memory.limit_in_bytes,假设进程使用内存超过这两个限制,内存回收时机和路径是怎么样的?
  4. 我们知道内核回收页面采用lru算法,同时memcg也有per node lru,这两个lru是什么关系?

被误解的cgroup内存限制

结论:Cgroup 内存范围包括进程RSS 及该进程首次触发加载进Page Cache 所占用的内存,但不包括Slab 部分。

我们怎么从源码确认page cache是被cgroup限制的呢?charge逻辑:我们知道新页面产生的时候,内核会charge增加cgroup内存使用的统计,所以最直接的方式我们看下read或者write产生page cache是否存在charge逻辑,如果存在说明进程pagecache也是被cgroup限制的。

上述函数的调用栈:

#0  try_to_free_mem_cgroup_pages (memcg=0xffff8880009ba000, nr_pages=1, gfp_mask=1125578, may_swap=true) at mm/vmscan.c:3326
#1  0xffffffff81422729 in try_charge (memcg=0xffff8880009ba000, gfp_mask=<optimized out>, nr_pages=<optimized out>) at mm/memcontrol.c:2703
#2  0xffffffff81425f56 in mem_cgroup_charge (page=0xffffea0000019240, mm=<optimized out>, gfp_mask=<optimized out>) at mm/memcontrol.c:6718
#3  0xffffffff8132bed0 in __add_to_page_cache_locked (page=0xffffea0000019240, mapping=0xffff888002826330, offset=65, gfp_mask=1125578, shadowp=0xffff888000b27650) at ./arch/x86/include/asm/current.h:15
#4  0xffffffff8132c224 in add_to_page_cache_lru (page=0xffffea0000019240, mapping=0xffff888002826330, offset=65, gfp_mask=1125578) at mm/filemap.c:922
#5  0xffffffff81344d9b in page_cache_readahead_unbounded (mapping=<optimized out>, file=<optimized out>, index=65, nr_to_read=<optimized out>, lookahead_size=<optimized out>) at mm/readahead.c:228
#6  0xffffffff81344eeb in __do_page_cache_readahead (mapping=0xffff888002826330, file=0xffff8880054fca00, index=<optimized out>, nr_to_read=32, lookahead_size=32) at mm/readahead.c:273
#7  0xffffffff8134518f in ra_submit (filp=<optimized out>, mapping=<optimized out>, ra=<optimized out>) at mm/internal.h:64
#8  ondemand_readahead (mapping=0xffff888002826330, ra=0xffff8880054fca98, filp=<optimized out>, hit_readahead_marker=<optimized out>, index=64, req_size=<optimized out>) at mm/readahead.c:551
#9  0xffffffff813454cd in page_cache_async_readahead (page=<optimized out>, req_count=<optimized out>, index=<optimized out>, filp=<optimized out>, ra=<optimized out>, mapping=<optimized out>)at mm/readahead.c:631
#10 page_cache_async_readahead (mapping=0xffff888002826330, ra=0xffff8880054fca98, filp=<optimized out>, page=0xffffffff8332f0b0 <cgrp_dfl_root+16>, index=<optimized out>, req_count=<optimized out>)at mm/readahead.c:604
#11 0xffffffff8132eba7 in generic_file_buffered_read (iocb=0xffff888000b27ad8, iter=0xffff888000b27a78, written=0) at mm/filemap.c:2220
#12 0xffffffff8132f674 in generic_file_read_iter (iocb=0xffff888000b27ad8, iter=0xffff888000b27a78) at mm/filemap.c:2520

可以看到read文件产生pagecache,最终要在add_to_page_cache_lru加入到address_space radix_tree和相对应的lru链表中,进而调用到mem_cgroup_charge逻辑,所以确认了我们的结论。

进程已运行后,加入Cgroup A中,已经使用的内存是否迁移统计入A 

当一个进程从一个cgroup移动到另一个cgroup时,默认情况下,该进程已经占用的内存还是统计在原来的cgroup里面,不会占用新cgroup的配额,但新分配的内存会统计到新的cgroup中(包括swap out到交换空间后再swap in到物理内存中的部分)。

我们可以通过设置memory.move_charge_at_immigrate让进程所占用的内存随着进程的迁移一起迁移到新的cgroup中。

enable: echo 1 > memory.move_charge_at_immigrate
disable:echo 0 > memory.move_charge_at_immigrate

注意: 就算设置为1,但如果不是thread group的leader,这个task占用的内存也不能被迁移过去。换句话说,如果以线程为单位进行迁移,必须是进程的第一个线程,如果以进程为单位进行迁移,就没有这个问题。

当memory.move_charge_at_immigrate被设置成1之后,进程占用的内存将会被统计到目的cgroup中,如果目的cgroup没有足够的内存,系统将尝试回收目的cgroup的部分内存(和系统内存紧张时的机制一样,删除不常用的file backed的内存或者swap out到交换空间上,如果回收不成功,那么进程迁移将失败。

memory.soft_limit_in_bytes和memory.limit_in_bytes内存回收时机

有了hard limit(memory.limit_in_bytes),为什么还要soft limit呢?hard limit是一个硬性标准,绝对不能超过这个值,而soft limit可以被超越,既然能被超越,要这个配置还有啥用?先看看它的特点

  1. 当系统内存充裕时,soft limit不起任何作用

  2. 当系统内存吃紧时,系统会尽量的将cgroup的内存限制在soft limit值之下(内核会尽量,但不100%保证)

从它的特点可以看出,它的作用主要发生在系统内存吃紧时,如果没有soft limit,那么所有的cgroup一起竞争内存资源,占用内存多的cgroup不会让着内存占用少的cgroup,这样就会出现某些cgroup内存饥饿的情况。如果配置了soft limit,那么当系统内存吃紧时,系统会让超过soft limit的cgroup释放出超过soft limit的那部分内存(有可能更多),这样其它cgroup就有了更多的机会分配到内存。

从上面的分析看出,这其实是系统内存不足时的一种妥协机制,给次等重要的进程设置soft limit,当系统内存吃紧时,把机会让给其它重要的进程。

注意: 当系统内存吃紧且cgroup达到soft limit时,系统为了把当前cgroup的内存使用量控制在soft limit下,在收到当前cgroup新的内存分配请求时,就会触发回收内存操作,所以一旦到达这个状态,就会频繁的触发对当前cgroup的内存回收操作,会严重影响当前cgroup的性能。

结论: 

soft_limit_in_bytes只有触发kswapd或者direct reclaim时候才会进行顺道的回收

limit_in_bytes:新页面产生时候,charge增加使用计数,如果超过limit_in_bytes就会回收。

全局LRU和memcg LRU的关系

结论:我们经常讨论的全局LRU其实对应root_mem_cgroup的per node LRU。

假设目前系统没有设置任何的cgroup,那么只有root_mem_cgroup这个memcg,只要配置CONFIG_CGROUP,内核初始化的时候就会初始化root cgroup。那么我们read/write产生pagecache情况下,新产生page加入lru的代码,看看到底加入的哪个LRU?

lru_cache_add--->__pagevec_lru_add--->pagevec_lru_move_fnstatic void pagevec_lru_move_fn(struct pagevec *pvec,void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),void *arg)
{int i;struct pglist_data *pgdat = NULL;struct lruvec *lruvec;unsigned long flags = 0;for (i = 0; i < pagevec_count(pvec); i++) {struct page *page = pvec->pages[i];struct pglist_data *pagepgdat = page_pgdat(page);if (pagepgdat != pgdat) {if (pgdat)spin_unlock_irqrestore(&pgdat->lru_lock, flags);pgdat = pagepgdat;spin_lock_irqsave(&pgdat->lru_lock, flags);}//内核通过mem_cgroup_page_lruvec获取加入的LRU,由于我们没有配置任何cgroup,//那么此时产生的page对应的lru就是root_mem_cgroup的pgdat这个node的 lrulruvec = mem_cgroup_page_lruvec(page, pgdat);(*move_fn)(page, lruvec, arg);}if (pgdat)spin_unlock_irqrestore(&pgdat->lru_lock, flags);release_pages(pvec->pages, pvec->nr);pagevec_reinit(pvec);
}

如上面代码注释,最终mem_cgroup_page_lruvec获取到page->memcg的pgdat node对应的lruvec,而这里page->memcg又是指向哪里的,由于目前系统没有配置任何的cgroup,这个page->memcg就指向root_mem_cgroup,那么page->memcg赋值的地方在哪里的,针对我们目前read pagecache这种场景,最终是在mm/memcontrol.c :commit_charge里面,调用栈:

remote Thread 1 In: mem_cgroup_charge                                                                                                                                            Line: 6723 PC: 0xffffffff8142f6cd 
#0  commit_charge (page=<optimized out>, memcg=<optimized out>)	at mm/memcontrol.c:6723
#1  mem_cgroup_charge (page=0xffffea00000c7600,	mm=<optimized out>, gfp_mask=<optimized out>) at mm/memcontrol.c:6723
#2  0xffffffff81330f80 in __add_to_page_cache_locked (page=0xffffea00000c7600, mapping=0xffff888006245e80, offset=4, gfp_mask=1125578, shadowp=0xffff888006557650) at ./arch/x86/include/asm/current.h:15
#3  0xffffffff813312d4 in add_to_page_cache_lru (page=0xffffea00000c7600, mapping=0xffff888006245e80, offset=4,	gfp_mask=1125578) at mm/filemap.c:922
#4  0xffffffff8134a0eb in page_cache_readahead_unbounded (mapping=<optimized out>, file=<optimized out>, index=4, nr_to_read=<optimized out>, lookahead_size=<optimized out>) at mm/readahead.c:228
#5  0xffffffff8134a25b in __do_page_cache_readahead (mapping=0xffff888006245e80, file=0xffff88800548b640, index=<optimized out>, nr_to_read=32,	lookahead_size=16) at mm/readahead.c:273
#6  0xffffffff8134a4ff in ra_submit (filp=<optimized out>, mapping=<optimized out>, ra=<optimized out>)	at mm/internal.h:64
#7  ondemand_readahead (mapping=0xffff888006245e80, ra=0xffff88800548b6d8, filp=<optimized out>, hit_readahead_marker=<optimized out>, index=0,	req_size=<optimized out>) at mm/readahead.c:551
#8  0xffffffff8134aac8 in page_cache_sync_readahead (req_count=<optimized out>,	index=<optimized out>, filp=<optimized out>, ra=<optimized out>, mapping=<optimized out>) at mm/readahead.c:585
#9  page_cache_sync_readahead (mapping=<optimized out>,	ra=0xffff88800548b6d8, filp=0xffff88800548b640,	index=<optimized out>, req_count=<optimized out>) at mm/readahead.c:567
#10 0xffffffff81333bf1 in generic_file_buffered_read (iocb=0xffff888006557ad8, iter=0xffff888006557a78,	written=0) at mm/filemap.c:2208
#11 0xffffffff81334776 in generic_file_read_iter (iocb=0xffff888006557ad8, iter=0xffff888006557a78) at mm/filemap.c:2520

commit_charge如下:

参考:

Linux内核mem_cgroup浅析-wzzushx-ChinaUnix博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135090.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】LinkedList与链表

文章目录 1. ArrayList的缺陷2. 链表2.1 链表的概念及结构2.2 链表的实现1.链表的功能2.初始化链表3.实现功能接口3.1头插添加元素3.2尾插法添加新元素3.3找到下标的前驱节点3.4指定位置插入元素3.5指定元素是否存在3.6找到指定元素的前驱节点3.7删除指定节点3.8删除所有元素为…

Flutter图标

https://fluttericon.cn/ Flutter 内置了丰富的图标。 Icon(Icons.ac_unit)

linux内核分析:线程和进程创建,内存管理

lec18-19:进程与线程创建 lec20-21虚拟内存管理 内核代码,全局变量这些只有一份,但是内核栈有多份,这可能就是linux线程模型1对1模式的由来。通过栈来做的 x86 CPU支持分段和分页(平坦内存模式)两种 分段,选择子那里就有特权标记了

高速DSP系统设计参考指南(一)高速DSP设计面临的挑战

&#xff08;一&#xff09;高速DSP设计面临的挑战 1. 概述2. 一般挑战3. DSP音频系统的挑战4. 视频系统的挑战5. DSP通信系统面临的挑战 资料参考来自TI官网和网络。 1. 概述 DSP芯片&#xff0c;也称数字信号处理器&#xff0c;是一种具有特殊结构的微处理器。DSP芯片的内部…

【开发】视频监控平台EasyCVR分组批量绑定/取消通道功能的后端代码设计逻辑介绍

视频监控平台/视频存储/视频分析平台EasyCVR基于云边端一体化管理&#xff0c;可支持视频实时监控、云端录像、云存储、磁盘阵列存储、回放与检索、智能告警、平台级联等功能。安防监控平台在线下场景中应用广泛&#xff0c;包括智慧工地、智慧工厂、智慧校园、智慧社区等等。 …

二进制 Deploy Kubernetes v1.23.17 超级详细部署

文章目录 1. 预备条件2. 基础配置2.1 配置root远程登录2.2 配置主机名2.3 安装 ansible2.4 配置互信2.5 配置hosts文件2.6 关闭防firewalld火墙2.7 关闭 selinux2.8 关闭交换分区swap2.9 修改内核参数2.10 安装iptables2.11 开启ipvs2.12 配置limits参数2.13 配置 yum2.14 配置…

浅析AI视频智能分析系统人脸检测算法的应用与特点

AI人脸检测算法可以提取人脸和服装的特征&#xff0c;并将其分类为有用的类别&#xff0c;例如性别、年龄和服装颜色。通过搜索这些丰富的属性信息&#xff0c;可以帮助我们轻松找到目标人物&#xff0c;比如通过人脸以图搜图、人脸布控等等。 如何搭建重点部位人脸识别动态布控…

Fuxploider:一款针对文件上传漏洞的安全检测与研究工具

Fuxploider:一款针对文件上传漏洞的安全检测与研究工具 1.概述2. 工具使用1.概述 Fuxploider是一款功能强大的开源渗透测试工具,该工具专门针对文件上传漏洞而设计,可以帮助广大研究人员以自动化的方式检测和利用目标站点文件上传表单中的安全问题 由于该工具基于Python 3…

elasticsearch18-自动补全实战

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

HUAWEI华为MateBook X Pro 2021款 i7 集显(MACHD-WFE9Q)原装出厂Win10系统20H2

华为笔记本电脑原厂系统自带指纹驱动、显卡驱动、声卡驱动、网卡驱动等所有驱动、出厂主题壁纸、系统属性华为专属LOGO标志、Office办公软件、华为电脑管家等预装程序 链接&#xff1a;https://pan.baidu.com/s/1oeSM0ciwyyRIKms5tR4SNA?pwdo2gq 提取码&#xff1a;o2gq

上海长宁来福士P2.5直径4米无边圆形屏圆饼屏圆面屏圆盘屏平面圆屏异形创意LED显示屏案例

长宁来福士广场是一个大型广场&#xff0c;坐落于上海中山公园商圈的核心区域&#xff0c;占地逾6万平方米&#xff0c;其中地上总建筑面积近24万平方米&#xff0c;总投资额约为96亿人民币。 LED圆形屏是根据现场和客户要求定制的一款异形创意LED显示屏&#xff0c;进行文字、…

SkyWalking快速上手(一)——安装单机版SkyWalking、使用SkyWalking

文章目录 什么是SkyWalking为什么选择SkyWalking安装步骤前置条件环境要求下载 SkyWalking 配置 SkyWalkingSkywalking 使用Agent 配置Collector 配置 启动 SkyWalking配置SkyWalking代理 SkyWalking的监控功能分布式调用链追踪性能指标监控告警和报警 总结 什么是SkyWalking …

vue3中使用el-upload + tui-image-editor进行图片处理

效果如下 看之前请先看上一篇《vue3中使用组件tui-image-editor进行图片处理》中的 1、第一步安装 2、第二部封装组件 本篇只是在这基础上结合el-upload使用组件 3、第三步结合el-upload使用组件 <template><el-dialog:title"dialogTitle":modelValue&qu…

常见的API

常见的 API Math 从 JDK 版本 1 开始的, 用来计算的一些方法 这里面定义了两个常量的 PI 和 E 这两个是最接近 pi 的值和最接近对数的值 Abs (int a ) 取绝对值Ceil (double a)向上取整Floor (double a )向下取整Round (float a)四舍五入Max (int a, int b) 取最大值Pow (dou…

【微信小程序】文章设置

设置基本字体样式&#xff1a;行高、首行缩进 font-size: 32rpx;line-height: 1.6em;text-indent: 2em;padding: 20rpx 0;border-bottom: 1px dashed var(--themColor); 两端对齐 text-align: justify; css文字两行或者几行显示省略号 css文字两行或者几行显示省略号_css…

【C进阶】指针和数组笔试题解析

做题之前我们先来回顾一下 对于数组名的理解&#xff1a;除了以下两种情况&#xff0c;数组名表示的都是数组首元素的地址 &#xff08;1&#xff09;sizeof&#xff08;数组名&#xff09;&#xff1a;这里的数组名表示整个数组 &#xff08;2&#xff09;&&#xff08;数…

Kakfa - Producer机制原理与调优

Producer是Kakfa模型中生产者组件&#xff0c;也就是Kafka架构中数据的生产来源&#xff0c;虽然其整体是比较简单的组件&#xff0c;但依然有很多细节需要细品一番。比如Kafka的Producer实现原理是什么&#xff0c;怎么发送的消息&#xff1f;IO通讯模型是什么&#xff1f;在实…

Prometheus黑盒测试模块,监控TCP端口+ HTTP/HTTPS路由状态

文章目录 一、黑盒测试使用场景二、安装blackbox-exporter三、监控TCP端口四、监控HTTP/HTTPS路由五、最后分享几款Grafana模板 一、黑盒测试使用场景 官方下载地址 blackbox-exporter是Prometheus官方提供的一个黑盒测试的解决方案&#xff0c;可用于以下使用场景&#xff1a…

大数据-玩转数据-Flink恶意登录监控

一、恶意登录 对于网站而言&#xff0c;用户登录并不是频繁的业务操作。如果一个用户短时间内频繁登录失败&#xff0c;就有可能是出现了程序的恶意攻击&#xff0c;比如密码暴力破解。 因此我们考虑&#xff0c;应该对用户的登录失败动作进行统计&#xff0c;具体来说&#x…

SpringBoot启动方式

SpringBoot启动方式 springboot的启动经过了一些一系列的处理&#xff0c;我们先看看整体过程的流程图 SpringBoot的启动方式 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><…