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.mzph.cn/news/83379.shtml

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

相关文章

Vue的路由使用,Node.js下载安装及环境配置教程 (超级详细)

前言&#xff1a; 今天我们来讲解关于Vue的路由使用&#xff0c;Node.js下载安装及环境配置教程 一&#xff0c;Vue的路由使用 首先我们Vue的路由使用&#xff0c;必须要导入官方的依赖的。 BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务https://www.bootcdn.cn/ <…

IP协议的相关特性

IP协议相关特性 报头结构 报文结构解释 4位版本号:指定IP协议的版本,对于IPV4来说,就是四位. 4位首部长度:IP头部的长度是多少个32bit,也就是Length4的字节数,4bit表示的最大的数是15,因此IP头部最大长度是60. 8位服务类型:3位优先权字段&#xff08;已经弃用&#xff09;&…

安全生产知识竞赛活动小程序界面分享

安全生产知识竞赛活动小程序界面分享

linux内核分析:进程通讯方式

信号 一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。 1.执行默认操作。Linux 对每种信号都规定了默认操作,例如,上面列表中的 Term,就是终止进程的意思。Core 的意思是 Core Dump,也即终止进程后,通过 Core Dump 将当前进程的运行状态保存在文件里面…

Python画图系列——折线图

好看的折线图 import numpy as np import matplotlib.pyplot as plt# 生成随机数据 # np.random.seed(42) # 设置随机种子以确保可重复性 sample_numbers np.arange(1, 21) # 生成1到20的样本编号random_data np.random.rand(20) # 生成20个随机数&#xff0c;范围在0到1之…

淘宝商品详情数据采集

淘宝商品详情数据采集的方法如下&#xff1a; 确定采集目标&#xff1a;明确要采集的商品信息&#xff0c;如商品标题、价格、销量、评论、图片等。选择采集工具&#xff1a;可以选择Scrapy框架、Java的WebMagic框架等。编写爬虫程序&#xff1a;进入目标文件夹&#xff0c;输…

无涯教程-JavaScript - POWER函数

描述 POWER函数返回加到幂的数字的输出。 语法 POWER (number, power)争论 Argument描述Required/OptionalNumber 基数。 它可以是任何实数。 RequiredPowerThe exponent to which the base number is raised.Required Notes 可以使用" ^"运算符代替POWER来指示…

八一书《乡村振兴战略下传统村落文化旅游设计》许少辉瑞博士生辉少许——2023学生开学季许多少年辉光三农

八一书《乡村振兴战略下传统村落文化旅游设计》许少辉瑞博士生辉少许——2023学生开学季许多少年辉光三农

二、搭建Java环境

搭建Java环境 搭建Java环境1.1.下载JDK1.2.在Win10下配置JDK环境 —————————————————————————————————————————————————— ———————————————————————————————————————————————…

python 冒泡排序以及鸡尾酒排序

冒泡排序 就是一次循环&#xff0c;两两数据比较&#xff0c;将最大值放入末尾&#xff0c;此时找到了最大值&#xff0c;然后进行二次三次等循环&#xff0c;直到排序结束 冒泡排序代码 def bubble_sort_v1(array[]):for i in range(len(array)-1):for j in range(len(arra…

【CANoe/CANalyzer脚本】通过CAPL发送NM帧报文测试网络管理

优质博文推荐阅读&#xff08;单击下方链接&#xff0c;即可跳转&#xff09;&#xff1a; Vector工具链 CAN Matrix DBC CAN Matrix Arxml 一、在CANoe/CANalyzer中创建Program Node 在Measurement Setup中&#xff0c;创建Program Node&#xff1b; 接着导入下面的Demo:N…

D数树,牛客小白月赛78,思维

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K 64bit IO Format: %lld 题目描述 “开导&#xff01;” 众所周知&#…

echarts 轴文字内容太长导致显示不全解决方案

情况&#xff1a;Y轴内容是文字&#xff0c;内容太长可能会系那是不全。调整grid会导致短数据前的留白过于多。 解决方案&#xff1a; 方法一&#xff1a; 首先可以配置grid自适应 &#xff0c; containLabel 属性设置true &#xff08;这常用于『防止标签溢出』的场景&…

树、二叉树、森林的相互转化

目录 一、树>二叉树 二、二叉树>树 三、二叉树>森林 四、森林>二叉树 一、树>二叉树 ①兄弟相连 ②断开每个节点除与左孩子之外的连线 ③调整树为二叉树 二、二叉树>树 ①如果一个节点有父节点&#xff0c;将其右孩子与父节点相连&#xff0c;右孩子…

3D模型格式转换工具HOOPS Exchange协助Epic Games实现CAD数据轻松导入虚幻引擎

一、面临的挑战 Epic Games最为人所知的身份可能是广受欢迎的在线视频游戏Fortnite的开发商&#xff0c;但它也是虚幻引擎背后的团队&#xff0c;虚幻引擎是一种实时3D创作工具&#xff0c;为世界领先的游戏提供动力&#xff0c;并且也被电影电视、建筑、汽车、制造、模拟等领…

Python计算机二级知识点整理

1.当一个进程在运行过程中释放了系统资源后要调用 唤醒进程原语 唤醒进程原语是把进程从等待队列里移出到就绪队列并设置进程为就绪状态&#xff0c;当一个进程在运行过程中释放了系统资源后进入就绪状态&#xff0c;调用唤醒进程原语。 2. 3. 4.在希尔排序法中&#x…

windows常见的命令行操作

1.查看网络 ipconfig 2.根据任务id关闭进程 1.模糊搜索服务 tasklist | findstr QQ 2.根据模糊搜索结果&#xff0c;获取对应的pid taskkill /F /PID 5128 其他常见命令 若不清楚参数&#xff0c;使用&#xff08;命令 /?&#xff09;即可查看help

Day 02 python学习笔记

python运算符 算术运算符 混合运算的优先级&#xff1a; () > ** * / // % 高于 - 赋值运算符 - * / ** a 1 > a 3 > a a 3 其余同理 注意&#xff1a; python没有自增自减 &#xff08;a a a-- --a&#xff0…

Android EditText筛选+选择功能开发

在日常开发中经常会遇到这种需求&#xff0c;EditText既需要可以筛选&#xff0c;又可以点击选择。这里筛选功能用的是AutoCompleteTextView&#xff0c;选择功能使用的是第三方库https://github.com/kongzue/DialogX。 Android AutoCompleteTextView(自动完成文本框)的基本使用…

go语言 最小堆 最大堆 堆排序

建堆 调整 删除 先根据数组构建完全二叉树 从第一个非叶结点开始 调整为大跟堆&#xff0c;这里就是图里面的1结点开始调整 代码如下&#xff1a; func findKthLargest(nums []int, k int) int {heapSize : len(nums)buildMaxHeap(nums,heapSize)for i : len(nums) - 1; i &…