linux 内存回收mglru算法代码注释2

news2024/9/21 11:15:53

mglru与原lru算法的兼容

旧的lru算法有active与inactive两代lru,可参考linux 内存回收代码注释(未实现多代lru版本)-CSDN博客

新的算法在引入4代lru的同时,还引入了tier的概念。

新旧算法的切换的实现在lru_gen_change_state,当开启mglru时,调用fill_evictable,将active list 与 inactive list 的folio迁移到 mglru上(mglru的组织方式是:lruvec[gen][type][zone]),如果是关闭mglru,则调用drain_evictable,将mglru的folio迁移回active/inactive list两代的情况。

当开启mglru时,原有shrink_node与shrink_lruvec的路径会短路,主要体现在两个地方,对于全局的回收直接调用lru_gen_shrink_node,对于某个memory group 的回收会间接调用lru_gen_shrink_lruvec:

shrink_node
    if (lru_gen_enabled() && root_reclaim(sc)) {
	    lru_gen_shrink_node(pgdat, sc);
	    return;
    }

shrink_lruvec
	if (lru_gen_enabled() && !root_reclaim(sc)) {
		lru_gen_shrink_lruvec(lruvec, sc);
		return;
	}

真正做页的回收的逻辑还是在shrink_folio_list。 

mglru与原lru算法的差别

与旧的lru算法区别,主要有三个方面:1、修改了一次扫描要扫的数量计算逻辑。2、修改了代与代之间转换的逻辑。3、添加了refault页的延迟回收机制

mglru的组织

每个numa node 有一个 pgdat 结构,上面绑定了为每个memory group准备了两代bin list,分别为young bin list和old bin list,第个bin list 上有8个bin,新加入的memory group会随机找一个 bin list 加入(lru_gen_online_memcg)。回收总是在old代上做,找一个bin list,从头扫描到尾。memory group 会随着它分配的内存大小和是否做了回收,在old与young的bin list 头尾上游走。(lru_gen_rotate_memcg),具体而言:

1、memory group 的内存超过 soft limit 时,将它移至同代的开头,下次可能回收它(lru_gen_soft_reclaim,MEMCG_LRU_HEAD)

2、新加入的memory group会放在新代的结尾处,第一次扫描发现页数少于2^priority或是第一次扫描发现页数在low水位线以下时,会放在新代的结尾处(MEMCG_LRU_TAIL)

3、当第一次扫描发现内存在min水位以下,或第二次扫描发现上次扫描是小于2^priority的,或是每次扫描完足够页数时会把最后一个扫描的memory group 移至新代(MEMCG_LRU_YOUNG)。

4、在移除一个memory group时,需要回收全部内存,会把它放在old代(lru_gen_offline_memcg,MEMCG_LRU_OLD)

bin list 中每一项是memory group的lruvec指针。

lruvec内部分成了4代,每代有两个type:文件or匿名,每个type又维护了每个zone上的页框,如下:

// 找一个 group 对应在某个 node 上的lru
lruvec = &memcg->nodeinfo[pgdat->node_id]->lruvec.lrugen;
// 遍历一个 node 上某个 binlist 的 lru 
lrugen = pgdat->memcg_lru.fifo[gen][bin];
// lru 内的页框
lrugen->folios[gen][type][zone]

扫描数量

原有的swappiness表示回收匿名页与文件页的加权,取值1~200,值越小越支持从匿名页回收。新算法计算扫描数量的方法变了,只根据swappiness有无赋值来决定要不要计算扫描匿名页的数量,文件页一定会扫描回收。计算的方式也比较粗暴:total >> sc->priority;具体计算逻辑在get_nr_to_scan->should_run_aging。

代际转换

如果在should_run_aging计算时发现最新一代的页框数已经是总页框数的一半,或第三代的页框数小于总页框数的四分之一,就触发一次代际迭换,尝试发现young 页,把它们提升至最新代。代际迭换的代码在try_to_inc_max_seq。

try_to_inc_max_seq():
    // 硬件不支持自动标记access flag
    if (!should_walk_mmu()) {
		iterate_mm_list_nowalk(lruvec, max_seq);
		return;
	}
  // 尝试扫描 hot pmd 中的 young 页。	
  do {
		is_last = iterate_mm_list(lruvec, walk, &mm);
		if (mm)
			walk_mm(lruvec, mm, walk);
	} while (mm);
   // 这一代扫描结束,更新代际
	if (is_last)
		inc_max_seq(lruvec, can_swap, force_scan);

如果硬件支持自动在页表记录访问标记,则扫描一遍(扫描的实现在try_to_inc_max_seq->walk_mm->walk_pgd_range->walk_pud_range->walk_pmd_range->walk_pte_range),通过检查bloom filter,找到标记为hot的pmd,访问pmd中全部pte,将标记脏的pte对应页框标记为脏,并更新至最新代。这里说的bloom filter标记了平均每个cacheline中young页数大于1的pmd,只需要对这些pmd的全部pte中young 页的扫描,并标记脏和更新代数,因为这个pmd范围的young页多,是个热点区,意味着后面可能还会产生hot页。如果硬件不支持自动设置访问标记,就不能在这个地方扫了,而要等到建立rmap时,folio_referenced_one->lru_gen_look_around

bloom filter的设置有两个途径,一个是在上面说的扫描全部pte之后,计算young页数/total页数大于cacheline中能装下的pte数(或者说是不是平均每个cacheline都有一个pte项对应了young页,实现在suitable_to_scan);另一个是在shrink_folio_list时,会找一个页框映射的次数(folio_referenced),会调一次lru_gen_look_around,尝试看下这个pte对应的pmd中全部pte,同样是在标记完脏页、统计完young页数时,计算young页数/total页数大于cacheline中能装下的pte数,并把young 标记清掉。

这个过程大概代码如下:


walk_pmd_range():
{
restart:
	for pmd_i in start_addr.. end_addr:
        // 检查是不是hot pmd
        if (!test_bloom_filter(max_seq, pmd_i))
             continue;
        // 检查hot pmd的所有pte中的脏页,并统计young的页数和清空young标记(young 指最近有访问),计算它还是不是hot pmd
		is_still_hot = walk_pte_range(addr, pmd_end_addr);
        // 如果是hot的pmd,则在bloom filter 标记一下,下一轮(代)扫描时再检查一次这个pmd
        if (is_still_hot)
		     update_bloom_filter(max_seq + 1, pmd + i);
	}
	if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
		goto restart;
}

walk_pte_range():
    new_gen = lru_gen_from_seq(walk->max_seq);
restart:
	for pte_i in start_addr.. end_addr:           
    {
	    // 硬件标记pte脏的,但页框没有标记脏,且这是文件页或未换出的匿名页,则在页框上标记下脏
		if (pte_dirty(ptent) && !folio_test_dirty(folio) &&
		    !(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
		      !folio_test_swapcache(folio)))
			folio_mark_dirty(folio);

        
     // 将这一页框更新到最新代
		old_gen = folio_update_gen(folio, new_gen);
     // 更新统计walk->nr_pages[old_gen][type][zone] 和 walk->nr_pages[new_gen][type][zone]
		if (old_gen >= 0 && old_gen != new_gen)
			update_batch_size(walk, folio, old_gen, new_gen);
	}

	if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
		goto restart;
    // 计算young页数/total页数大于cacheline中能装下的pte数(或者说是不是平均每个cacheline都有一个pte项对应了young页)
	return suitable_to_scan(total, young);
}

Refault页的延迟回收

refault指缺页读入后又换出又读入。mglru引入tier概念,组织形式为lrugen->refaulted[hist][type][tier]。为file 和anon类型的页,维护了4代统计直方图(hist),每个直方图中有4个范围(tier),分别统计了本轮回收中访问了1次,2次,4次,8次的页数。

当触发refault时,会统计累加本轮回收中,已经refault这么多次的页数。(lru_gen_refault)

lru_gen_refault():
	// recent 指refault与上次回收在同一代内
	recent = lru_gen_test_recent(shadow, type, &lruvec, &token, &workingset);
	// 总共有4代histogram,根据当前代数算出它在那个histogram中
	hist = lru_hist_from_seq(READ_ONCE(lrugen->min_seq[type]));
	// 每代有4个tier,tier的index = log2(本轮扫描中这页的 access 数),即分别为访问1次,2次,4次,8次的tier。
	tier = lru_tier_from_refs(refs);
    // 统计累加本轮扫描过程中发生 2^tier 次 refault 的页数。
	atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);

在决定是否回收页时,evict_folios->isolate_folios,会平衡本轮发生refault 的页数与回收+延时回收页数的比值,计算一个控制值(refaulted/(evicted+protected)),可以理解为发生refault的频繁程度。如果发生n次refault的频繁程度达到了发生1次refault频繁程度的2倍,则发生n次以上refault的页都不再回收。

isolate_folios():
    // 计算refault次数超过多少后不再释放
    tier_idx = get_tier_idx(lruvec, type);

isolate_folios->scan_folios->sort_folio():
  // 本轮扫描中 refault 次数超过2^tier_idx 次的页不再释放,而是推到下一代
    if (tier > tier_idx) {
        // 将页放在下一次lru尾(回收是从本代的头开始的)
		gen = folio_inc_gen(lruvec, folio, false);
		list_move_tail(&folio->lru, &lrugen->folios[gen][type][zone]);

        // 累加本代中不释放页的页数
     int hist = lru_hist_from_seq(lrugen->min_seq[type]);
		WRITE_ONCE(lrugen->protected[hist][type][tier - 1],
			   lrugen->protected[hist][type][tier - 1] + delta);
		return true;
	}

在回收过程中,每完成一次分离出回收页的计算后(isolate_folios),会将这一代的统计值更新为新值与历史值的滑动平均值。

在一轮回收结束时,会调inc_max_seq将下一轮回收的代统计值清空,为最新代的统计留出位置。

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

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

相关文章

远程网络监控(RMON)

远程网络监控是一个使 IT 团队能够获得远程网络可见性的过程&#xff0c;它涉及主动监控网络以帮助网络无缝运行&#xff0c;这些监控远程网络的系统提供对性能的实时洞察&#xff0c;及时检测问题并在影响最终用户之前解决问题。这样&#xff0c;远程网络虽然相距遥远&#xf…

开源与闭源

我的观点&#xff1a; 开源与闭源软件都有各自的优势和劣势&#xff0c;没有绝对的对错之分。.. 一、开源和闭源的优劣势比较 开源的好处与劣处 优势&#xff1a; 创新与合作&#xff1a;开源软件能够吸引更多的开发者参与到项目中来&#xff0c;促进创新和合作。开放的源代码…

昇腾Atlas 200I DK A2实现安全帽识别

文章目录 环境依赖编译测试总结 环境依赖 软件版本说明获取方式mxVision5.0.RC2mxVision软件包获取方式Ascend-CANN-toolkit6.2.RC2Ascend-cann-toolkit开发套件包获取方式Ubuntu22.04 代码仓库地址&#xff1a; https://gitee.com/ascend/ascend_community_projects/tree/31…

使用 Lhotse 高效管理音频数据集

Lhotse 是一个旨在使语音和音频数据准备更具灵活性和可访问性的 Python 库&#xff0c;它与 k2 一起&#xff0c;构成了下一代 Kaldi 语音处理库的一部分。 主要目标&#xff1a; 1. 以 Python 为中心的设计吸引更广泛的社区参与语音处理任务。 2. 为有经验的 Kaldi 用户提供…

【ELK02】ES的重要核心概念和索引常用操作-索引文档管理、文档搜索

一、ELASTICSEARCH核心概念 1.ES中的重要概念 1.1索引(index) 类似于关系型数据中的库-database,一个es的集群中可以有多个索引,每个索引都是一批独立的存储数据,按照一定的数据结构保存,方便查询. 1.2类型(type) 类似于关系型数据库中的表格-table,一个索引中可以有多个类…

Leetcode---372周赛

题目列表 2937. 使三个字符串相等 2938. 区分黑球与白球 2939. 最大异或乘积 2940. 找到 Alice 和 Bob 可以相遇的建筑 一、使三个字符串相等 这题把题目意思读懂&#xff0c;正常模拟就行&#xff0c;简单来说就是看三个字符串的最长公共前缀有多长&#xff0c; 代码如下…

Redis Cluster主从模式详解

在软件的架构中&#xff0c;主从模式&#xff08;Master-Slave&#xff09;是使用较多的一种架构。主&#xff08;Master&#xff09;和从&#xff08;Slave&#xff09;分别部署在不同的服务器上&#xff0c;当主节点服务器写入数据时&#xff0c;同时也会将数据同步至从节点服…

【计算机网络笔记】数据链路层——差错编码

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

亚马逊云科技re:Invent大会:云计算与生成式AI共筑科技新局面,携手构建未来

随着科技的飞速发展&#xff0c;云计算和生成式 AI 已经成为了推动科技进步的重要力量。这两者相互结合&#xff0c;正在为我们创造一个全新的科技局面。 亚马逊云科技的re:Invent大会再次证明了云计算和生成式AI的强大结合正在塑造科技的新未来。这次大会聚焦了云计算的前沿技…

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二)

通过ros系统中websocket中发送sensor_msgs::Image数据给web端显示(二) mp4媒体流数据 #include <ros/ros.h> #include <signal.h> #include <sensor_msgs/Image.h> #include <message_filters/subscriber.h> #include <message_filters/synchroniz…

DNS 区域传输 (AXFR)

漏洞描述 docker环境搭建 使用 AXFR 协议的 DNS 区域传输是跨 DNS 服务器复制 DNS 记录的最简单机制。为了避免在多个 DNS 服务器上编辑信息&#xff0c;可以在一台服务器上编辑信息&#xff0c;并使用 AXFR 将信息复制到其他服务器。但是&#xff0c;如果您不保护您的服务器&…

图论——最小割问题

Capacity&#xff08;S&#xff0c;T) Min-Cut(通俗的说就是用最小的力气隔断&#xff09; 最小割并不唯一 最大流最小割定理 对于一个网络流问题&#xff0c;最大流的流量最小割的容量 寻找最小割 可以使用Edmonds-karp or Dinic algorithm 首先寻找任意一个最大流&#xff…

浏览器是什么

浏览器是什么 本文简要介绍浏览器的功能和组成。 浏览器&#xff08;Web Browser&#xff09;是一种用于访问和浏览互联网上的网页和资源的软件应用程序。它是用户与互联网交互的主要工具之一。 浏览器通过使用网络协议&#xff08;如HTTP、HTTPS等&#xff09;与远程服务器通…

杰发科技AC7801——ADC软件触发的简单使用

前言 7801资料读起来不是很好理解&#xff0c;大概率是之前MTK的大佬写的。在此以简单的方式进行描述。我们做一个简单的规则组软件触发Demo。因为规则组通道只有一个数据寄存器&#xff0c;因此还需要用上DMA方式搬运数据到内存。 AC7801的ADC简介 7801的ADC是一种 12 位 逐…

LeetCode Hot100 108.将有序数组转为二叉搜索树

题目&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 方法&#xff1a; class Solution {public…

两个数组的交集(C++实现)

两个数组的交集 题目思路代码代码讲解总结和意义交集和差集思路意义 题目 思路 利用set容器的特性对输入的两个整数向量进行排序和去重操作&#xff0c;然后使用两个迭代器在有序的两个set容器中进行遍历&#xff0c;找出两个向量的交集&#xff0c;并将交集元素存储在结果向量…

CSS:浏览器设置placeholder样式 / 微信小程序设置placeholder样式

一、web 设置placeholder 设置浏览器的placeholder样式 ::-webkit-input-placeholder { /* WebKit browsers */color: #999; } :-moz-placeholder { /* Mozilla Firefox 4 to 18 */color: #999; } ::-moz-placeholder { /* Mozilla Firefox 19 */color: #999; } :-ms-input-p…

JavaScript编程基础 – 对象

JavaScript编程基础 – 对象 JavaScript Programming Essentials – Object 本文简要介绍JavaScript面向对象编程&#xff0c;如何实现其中的对象以及实例演示&#xff0c;希望对大家学习JavaScript有所帮助。 1. 面向对象编程特点 面向对象编程(Object-Oriented Programmi…

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志文件配置

万字长文 - Python 日志记录器logging 百科全书 - 高级配置之 日志文件配置 前言 在 Python 的logging模块中&#xff0c;它不仅提供了基础的日志功能&#xff0c;还拥有一系列高级配置选项来满足复杂应用的日志管理需求。 说到logging 模块的高级配置&#xff0c;必须提及日…

STM32_7(ADC)

一、ADC ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁12位逐次逼近型ADC&#xff0c;1us转换时间输入电压范围&#xff1a;0~3.3V&#xff0c;…