当前位置: 首页 > news >正文

linux 内存回收代码注释(未实现多代lru版本)

1.shrink_node_memcgs

node_reclaim->__node_reclaim->shrink_node,这里调用shrink_node_memcgs回收后,要判断下是否分配内存速度比回收速度快太多,这种情况可能会尝试停一段时间等回收完成(reclaim_throttle),然后再看是否需要继续回收(should_continue_reclaim)。实际入口shrink_node_memcgs如下:

shrink_node_memcgs():
for each memory control group:/*** 按照指定的预留min/low内存,从上层节点瓜分实际分到的min/low预留内存大小。* 具体见:https://blog.csdn.net/qq_37517281/article/details/134466144*/mem_cgroup_calculate_protection()if (mem_cgroup_below_min(target_memcg, memcg)) {// 本group分配的未达到min预留内存,尝试下个groupcontinue;} else if (mem_cgroup_below_low(target_memcg, memcg)) {// 如果指定不对未达到low限制的group回收内存,则只标记下skipif (!sc->memcg_low_reclaim) {sc->memcg_low_skipped = 1;continue;}}// 尝试回收页与slabshrink_lruvec();shrink_slab();/*** 计算些时回收压力: 回归页/扫描页数×100* 大于60是中等压力,大于95是严重压力。*/vmpressure();

2.shrink_lruvec

shrink_lruvec 中会计算本次要扫描的页数,然后触发实际的扫描和页回收(shrink_list):实现如下:

shrink_lruvec():// 计算要扫描页数get_scan_count(lruvec, sc, nr);while 未扫描足够的页数且文件页或匿名页数量都不是0:for 2个 active lru list(file and anon):// 如果指定了may_deactivate,则尝试将部分hot 页移至cold 链表中shrink_active_list();for 2个 inactive lru list(file and anon):shrink_inactive_list();// 如果匿名页的cold页少,尝试deactivate 一些老的hot匿名页。shrink_active_list();

3.get_scan_count

 scan 四个lru链表页数计算方法如下(get_scan_count):

get_scan_count():/*** 先判断回收文件页还是匿名页,只扫描文件页的情况:* a.没有swap空间* b.一个cgroup的memory.swappness为0(swapness表示对换出匿名页与重加载文件页的cost的加权,cost 指scan过程中发现的不可换出的页数+换出页×32)* c.指定了回收缓存文件页(cache_trim_mode)* 只扫描匿名页的情况:* a.文件页很少* 同时回收文件页和匿名页的情况:* a.即将oom时,这时的priority为0(后面扫描时,对所有可扫描页的扫描比例为priority分之一)* 其它情况下,会计算文件页与匿名页扫描比例,比例的原始值控制在1/3 - 2/3之间* (当anon_cost为0时,ap 与 fp 的未加权的原始比是1:2),* 取决于扫描时的cost(scan过程中发现的不可换出的页数+换出页×32)* 乘上一个换出匿名页与加载文件页的io cost比(swapness/200)**/total_cost = sc->anon_cost + sc->file_cost;anon_cost = total_cost + sc->anon_cost;file_cost = total_cost + sc->file_cost;total_cost = anon_cost + file_cost;ap = swappiness * (total_cost + 1);ap /= anon_cost + 1;fp = (200 - swappiness) * (total_cost + 1);fp /= file_cost + 1;fraction[0] = ap;fraction[1] = fp;denominator = ap + fp;// 计算 4 个 lru list (active/inactive × anon/file)扫描数量: if (!sc->memcg_low_reclaim && low > min) {protection = low;sc->memcg_low_skipped = 1;} else {protection = min;}// scan = lru页数-组内预留页数scan = lruvec_size - lruvec_size * protection / (cgroup_size + 1);// priority 默认为 12scan >>= sc->priority;scan = mem_cgroup_online(memcg) ?div64_u64(scan * fraction[file], denominator) :DIV64_U64_ROUND_UP(scan * fraction[file],denominator);

4.shrink_active_list

将active 页移至inactive页的方法(shrink_active_list):

shrink_active_list():/*** 处理一批本cpu上的页状态转换请求(cpu_fbatches),和对页标记可否回收的请求(mlock_fbatch)* 1、新访问的页和最新分配的页会记在fbatches->lru_add上,处理函数是 lru_add_fn。* 2、writeback的页放在lru_rotate.fbatch,处理函数是 lru_move_tail_fn,将其放在inactive 的tail上,等待回收(页回收是从tail开始的)* 3、madvice 为don't need的页,或是free了的设备页、文件页,放在cpu_fbatches.lru_deactivate_file,处理函数是 lru_deactivate_file_fn * 会将dirty/writeback/clean页放inactive的head上* 4、匿名页比如内核自己使用并释放的页,放在 cpu_fbatches.lru_deactivate,处理函数是 lru_deactivate_fn 将页框放在 active 的head上* 5、huge page 是lazyfree的,放在cpu_fbatches.lru_lazyfree上,处理函数是 lru_lazyfree_fn 会将页当成clean页,放在inactive 的头上。*/lru_add_drain();// 从后向前扫描,跳过高于指定zone的区和cma的区,从这中间的区找可以unmap的页框,或未map的页框isolate_lru_folios();for 每个扫描出的页框:// 判断这个页框没有mlock的vma,且它的mapping也没有标记unevictableif (unlikely(!folio_evictable(folio))) {folio_putback_lru(folio);continue;}// 如果buffer的总数太多,且这个页框中有buffer,则尝试解除映射if (unlikely(buffer_heads_over_limit)) {if (folio_test_private(folio) && folio_trylock(folio)) {if (folio_test_private(folio))filemap_release_folio(folio, 0);folio_unlock(folio);}}// 尝试把code文件页加入active队列,把其它页加入inactive队列list_add(&folio->lru, &l_inactive);// 给evictable的页引用减1,引用不为0的加入到lru的inactive或active的lru中。引用为0则尝试释放,对页框中有超过1页的情况,调用页框绑定的destroy;对于只有一页的情况,攒起来在后面两个函数内释放。move_folios_to_lru()// 释放只有一页的页框mem_cgroup_uncharge_list();free_unref_page_list();

5.shrink_inactive_list

shrink_inactive_list():// 处理一批本cpu上的页状态转换请求(cpu_fbatches),和对页标记可否回收的请求(mlock_fbatch)lru_add_drain();// 从lru中切出要扫描的页isolate_lru_folios();// 将一些页换出shrink_folio_list();// 将没有迁移成功、没有换出成功、没有写回文件的页重新加回原lru链表,并将ref减1move_folios_to_lru();// 统计costlru_note_cost(lruvec, file, stat.nr_pageout, nr_scanned - nr_reclaimed);// 将ref减1后为0的页框释放掉mem_cgroup_uncharge_list(&folio_list);free_unref_page_list(&folio_list);

6.shrink_folio_list

shrink_inactive_list会将部分页回收、送入swap区,或迁移到其它numa node上。具体实现如下:

shrink_folio_list():
if (folio_test_writeback(folio)) {/*** 如果已经标记了writeback和reclasim,说明它在等io,* 这时需要activate它并继续scan来找其它inactive list上可以回收的folio* (如果是swap区的,将swapcache相关项清理掉)*/if (current_is_kswapd() &&folio_test_reclaim(folio) &&test_bit(PGDAT_WRITEBACK, &pgdat->flags)) {stat->nr_immediate += nr_pages;goto activate_locked;} else if (writeback_throttling_sane(sc) ||!folio_test_reclaim(folio) ||!may_enter_fs(folio, sc->gfp_mask)) {/*** 如果本次扫描不没指定可访问文件系统(__GFP_FS or __GFP_IO),* 且这个页还没标记可回收,就标记一下这个页框可回收并继续scan* 可能在下一次扫描时回收*/folio_set_reclaim(folio);stat->nr_writeback += nr_pages;goto activate_locked;} else {/*** 如果已经标记了页框回收,则已经扫描一遍了,只能等writeback结束*/folio_unlock(folio);folio_wait_writeback(folio);/* then go back and try same folio again */list_add_tail(&folio->lru, folio_list);continue;}
}
folio_check_references();
// 如果没有引用,则考虑可否直接回收,先尝试迁移到最近距离的下一个node(demote_folio_list,下一个node记录在全局的node_demotion中)
if (do_demote_pass &&(thp_migration_supported() || !folio_test_large(folio))) {list_add(&folio->lru, &demote_folios);folio_unlock(folio);continue;
}
// 看是否是匿名可进swap区的页(有页面映射且没有人为它建过swapcache),为它建立swapcache项
if (folio_test_anon(folio) && folio_test_swapbacked(folio)) {if (!folio_test_swapcache(folio)) {// 添加一个swap区的映射entryadd_to_swap();}
}
// 对于文件页和没有swapcache的匿名页,需要给它解除页表映射项
if (folio_mapped(folio)) {try_to_unmap(folio, flags);
}
// 对于dirty的页,只有kswapd进程可以直接回收,其它进程只能柡注reclaim,以防止stack overflow
if (folio_test_dirty(folio)) {// kswapd进程来回收了,需要把tlb刷下去try_to_unmap_flush_dirty();pageout();
}
// 如果这个页框还有buffer,尝试解除clean的和没有映射的buffer
if (folio_has_private(folio)) {filemap_release_folio(folio, sc->gfp_mask)
}

相关文章:

  • java_函数式接口
  • 浪潮信息云峦服务器操作系统KeyarchOS体验与实践
  • TensorFlow实战教程(二十八)-Keras实现BiLSTM微博情感分类和LDA主题挖掘分析
  • Flutter笔记:Matrix4矩阵变换与案例
  • 【前端】vue中合并表格行
  • MySQL 8.2 Command Line Client打开时一闪而过闪退问题
  • Vue 3.0 中重置 reactive 定义的响应式对象数据,恢复为初始值
  • 智能指针(Newbie Note)
  • 基于区域划分的GaN HEMT 准物理大信号模型
  • 第六章 SELinux
  • Golang http 请求如何设置代理
  • harmonyOS鸿蒙开发工具下载安装以及使用流程
  • 9、鸿蒙应用桌面图标外观和国际化
  • VMware三种网络模式
  • Ubuntu设设置默认外放和麦克风设备
  • 「译」Node.js Streams 基础
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【知识碎片】第三方登录弹窗效果
  • ERLANG 网工修炼笔记 ---- UDP
  • es6
  • iOS 颜色设置看我就够了
  • JSDuck 与 AngularJS 融合技巧
  • Netty 4.1 源代码学习:线程模型
  • ng6--错误信息小结(持续更新)
  • Python学习之路16-使用API
  • React Native移动开发实战-3-实现页面间的数据传递
  • Redux 中间件分析
  • Webpack 4 学习01(基础配置)
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 通过npm或yarn自动生成vue组件
  • 一道闭包题引发的思考
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • ​【已解决】npm install​卡主不动的情况
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​批处理文件中的errorlevel用法
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • #NOIP 2014#Day.2 T3 解方程
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (一)UDP基本编程步骤
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • **CI中自动类加载的用法总结
  • .NET CF命令行调试器MDbg入门(一)
  • .net core webapi 大文件上传到wwwroot文件夹
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET和.COM和.CN域名区别
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET是什么
  • .Net中的集合
  • ::before和::after 常见的用法