Linux内存管理:(六)页交换算法

文章说明:

  • Linux内核版本:5.0

  • 架构:ARM64

  • 参考资料及图片来源:《奔跑吧Linux内核》

  • Linux 5.0内核源码注释仓库地址:

    zhangzihengya/LinuxSourceCode_v5.0_study (github.com)

1. 引言

在Linux操作系统中,当内存充足时,内核会尽量多地使用内存作为文件缓存(page cache), 从而提高系统的性能。文件缓存页面会添加到文件类型的LRU链表中;当内存紧张时,文件缓存页面会被丢弃,或者把修改的文件缓存页面回写到存储设备中,与块设备同步之后便可释放出物理内存。现在的应用程序转向内存密集型,无论系统中有多少物理内存都是不够用的,因此Linux操作系统会使用存储设备作为交换分区,内核将很少使用的内存换出到交换分区,以便释放出物理内存,这个机制称为页交换(swapping),这些处理机制统称为页面回收(page reclaim)

在最近几十年操作系统的发展过程中,出现了很多页面交换算法,其中每个算法都有各自的优点和缺点。Linux内核中采用的页交换算法主要是经典LRU链表算法第二次机会(second chance)法

2. 经典LRU链表算法

LRU是Least Recently Used的缩写,意为最近最少使用。根据局部性原理,LRU假定最近不使用的页面在较短的时间内也不会频繁使用。在内存不足时,这些页面将成为被换出的候选者。内核使用双向链表来定义LRU链表,并且根据页面的类型将LRU链表分为LRU_ANON和LRU_FILE。每种类型根据页面的活跃性分为活跃LRU链表和不活跃LRU链表,所以内核中一共有如下5个LRU链表:

// 定义了各种 LRU 链表的类型
enum lru_list {// 不活跃匿名页面链表LRU_INACTIVE_ANON = LRU_BASE,// 活跃匿名页面链表LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,// 不活跃文件映射页面链表LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,// 活跃文件映射页面链表LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,// 不可回收页面链表LRU_UNEVICTABLE,NR_LRU_LISTS
};

LRU链表之所以要分成这样,是因为当内存紧缺时总是优先换出文件映射的文件缓存页面 (LRU_FILE链表中的页面),而不是匿名页面。因为大多数情况下,文件缓存页面不需要被回写到磁盘,除非页面内容修改了(称为脏页),而匿名页面总是要在写入交换分区之后,才能被换出。LRU链表按照内存节点配置,也就是说,每个内存节点中都有一整套LRU链表,因此内存节点的描述符数据结构(pglist_data)中有一个成员lruvec指向这些链表。枚举类型变量lru_list 列举出上述各种LRU链表的类型,lruvec数据结构中定义了上述各种LRU类型的链表:

// 定义了各种 LRU 链表
struct lruvec {struct list_head		lists[NR_LRU_LISTS];...
};// 内存节点的数据结构
typedef struct pglist_data {// 每个内存节点中都有一整套 LRU 链表,由 lruvec 指向struct lruvec		lruvec;
} pg_data_t;

万事从图说起,经典LRU链表算法如下图所示:

在这里插入图片描述

为了使读者有更真切的理解,下文将根据流程图围绕源代码进行讲解这个过程:

将页面加入 LRU 链表:

static void __lru_cache_add(struct page *page)
{// 这里使用了页向量数据结构,借助一个数组来保存特定数目的页,可以对这些页面执行同样的操作// 页向量会以“批处理的方式”执行,比单独处理一个页面的方式效率要高struct pagevec *pvec = &get_cpu_var(lru_add_pvec);get_page(page);// pagevec_add() 函数首先往 pvec->pages[] 数组里添加页面,// 如果没有空间了,则调用 __pagevec_lru_add() 函数把原有的页面添加到 LRU 链表中if (!pagevec_add(pvec, page) || PageCompound(page))__pagevec_lru_add(pvec);put_cpu_var(lru_add_pvec);
}void lru_cache_add(struct page *page)
{...__lru_cache_add(page);
}

lru_to_page(&lru_list)list_del(&page->lru)函数的组合用于从LRU链表中获取页面。其中,lru_to_page()的实现如下:

#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))

lru_to_page()使用了(head)->prev,表示从链表的末尾获取页面。因此,LRU链表实现了 FIFO算法。最先进入LRU链表的页面在LRU中的时间会越长,老化时间也越长。

在系统执行过程中,页面总是在活跃LRU链表和不活跃LRU链表之间转移,不是每次访问内存页面都会发生这种转移,而是发生的时间间隔比较长。随着时间的推移,这会导致—种热平衡,最不常用的页面将慢慢移动到不活跃LRU链表的末尾,这些页面正是页面回收中最合适的候选者。

3. 第二次机会法

当系统内存短缺时,LRU链表尾部的页面将会离开并被换出。当系统再需要这些页面时,这些页面会重新置于LRU链表的开头。显然,这个设计不是很巧妙,在换出页面时,没有考虑该页面是频繁 使用的,还是很少使用的。也就是说,频繁使用的页面依然会因为在LRU链表末尾而被换出

第二次机会法的改进是为了避免把经常使用的页面置换出去,设置了一个访问状态位(硬件控制的位,PTE_YOUNG),所以要检查页面的访问位。如果访问位是0,就淘汰这个页面;如果访问位是1,就给它第二次机会,并选择下一个页面来换出。当该页面得到第二次机会时,它的访问位被清零,如果该页面在此期间再次被访问过,则访问位设置为1。

Linux内核使用下面这两个标志位来是实现第二次机会法:

  • PG_active:表示该页面是否活跃
  • PG_referenced:表示该页面是否被引用过

mark_page_accessed() 函数将页面标记为活跃:

  • 如果 PG_active==0 && PG_referenced==1,则把该页面加入活跃LRU链表,并设置 PG_active=1,清除PG_reference 标志位
  • 如果 PG_active==0,则设置 PG_referenced 标志位

在扫描不活跃 LRU 链表时,page_check_references() 这个函数会被调用:

// page 表示要处理的物理页面的page数据结构
// sc 表示内部用来控制页面扫描的数据结构
static enum page_references page_check_references(struct page *page,struct scan_control *sc)
{...// page_referenced() 检查该页面访问、引用了多少个PTE(referenced_ptes)referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,&vm_flags);// TestClearPageReferenced() 函数返回该页面 PG_referenced 标志位的值(referenced_page),并且清除该标志referenced_page = TestClearPageReferenced(page);...if (referenced_ptes) {...// 在内存短缺的情况下,kswapd 巧妙地释放了短时间内只访问一次的大量文件缓存SetPageReferenced(page);// referenced_ptes > 1 表示那些第一次在不活跃LRU链表中的共享文件映射页面(共享文件缓存),// 它们应该晋升到活跃 LRU 链表中,因为它们应该在活跃 LRU 链表中多待一段时间,以便其他用户可以再次访问到。if (referenced_page || referenced_ptes > 1)return PAGEREF_ACTIVATE;...}...
}

page_referenced() 函数用于判断页面是否被访问过,并返回引用的PTE的个数,即访问引用这个页面的用户进程空间虚拟页面的个数,核心思想是利用 RMAP 系统来统计访问、引用 PTE 的用户个数:

int page_referenced(struct page *page,int is_locked,struct mem_cgroup *memcg,unsigned long *vm_flags)
{...// 定义 rmap_one() 函数的指针struct rmap_walk_control rwc = {.rmap_one = page_referenced_one,.arg = (void *)&pra,.anon_lock = page_lock_anon_vma_read,};*vm_flags = 0;// 判断 page->_mapcount 是否大于或等于 0if (!page_mapped(page))return 0;// 判断 page->mapping 是否有地址空间映射if (!page_rmapping(page))return 0;...// 遍历所有映射该页面的 PTErmap_walk(page, &rwc);*vm_flags = pra.vm_flags;...return pra.referenced;
}

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

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

相关文章

6592A便携式高精度光伏电池伏安特性测试仪

6592A便携式高精度光伏 电池伏安特性测试仪 光伏仪器 主要用于室外太阳能电池阵列/组件/电池片伏安特性测试 国产思仪 01 产品综述 6592A便携式高精度光伏电池伏安特性测试仪,主要用于室外太阳能电池阵列/组件/电池片伏安特性测试,能够方便、快速的…

系列三十三、如何将一个springboot jar做成批处理文件

一、将一个springboot jar做成批处理文件 1.1、需求 最近在写【Spring Cloud Alibaba】的系列文章,其中有一个部分是安装Sentinel控制台,使用命令执行完全没有问题,但是命令太长了,每次启动时都要找笔记,然后粘贴到命…

小兔鲜儿 uniapp - 项目打包

目录 微信小程序端​ 核心步骤​ 步骤图示​ 条件编译​ 条件编译语法​ 打包为 H5 端​ 核心步骤​ 路由基础路径​ 打包为 APP 端​ 微信小程序端​ 把当前 uni-app 项目打包成微信小程序端,并发布上线。 核心步骤​ 运行打包命令 pnpm build:mp-weix…

C语言编译器(C语言编程软件)完全攻略(第十三部分:VS2010使用教程(使用VS2010编写C语言程序))

介绍常用C语言编译器的安装、配置和使用。 十三、VS2010使用教程(使用VS2010编写C语言程序) 提示:VS2010 可以在 XP、Win7 和 Win8 下完美运行,但在 Win10 下可能会有兼容性问题,使用 Win10 的读者建议安装 VS2015 或…

四种“栈溢出检测方法”实现分析(2种纯软件、一种纯硬件、一种软硬件结合)

1、两种纯软件的栈溢出检测方法 参考博客:《freeRTOS的栈溢出检测机制》; 2、纯硬件:使用栈限制寄存器 2.1、工作逻辑分析 前提条件:使用满减栈硬件上提供栈限制寄存器(用SP_limit表示),可以…

HttpRunner自动化测试之实现参数化传递

参数化实现及重复执行 参数化测试:在接口测试中,为了实现不同组数据对同一个功能模块进行测试,需要准备多组测试数据对模块进行测试的过程。 在httprunner中可以通过如下方式实现参数化: 1、在YAML/JSON 中直接指定参数列表 2、…

2024年5大海外代理IP测评推荐

在跨境电商行业,想要发送安全运营跨境电商账号,多账号社媒推广,独立站SEO优化等等的业务都少不了代理IP工具。安全高质的代理IP可以保障你的网络活动安全、匿名、高效。 面对众多代理IP商,您是否很难做出购买决定?哪些…

FinGPT——金融领域开源大模型

文章目录 背景论文摘要相关工作大型语言模型(LLMs)和ChatGPT金融领域的LLMs为什么需要开源的金融LLMs? 以数据为中心的方法用于FinLLMs金融数据和独特特性应对处理金融数据的挑战 FINGPT 概述:FINLLM 的开源框架数据来源面向金融N…

C++进阶(一)继承

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、继承的概念及定义1、继承的概念2、继承定义1、定义格式2、继承关系和访问限定符3、继承基…

SSMBUG汇总

20240103 通用,驼峰命名法,mybatis。 mybatis入门程序中, // 获取对象的顺序为:SqlSessionFactoryBuild-》SqlSessionFactory-》SqlSessionSqlSessionFactoryBuilder sqlSessionFactoryBuilder new SqlSessionFactoryBuilder();I…

怎么选择一款好用的ERP企业管理软件?看这六点就够了!

在目前高度信息化的环境中,ERP系统已成为生产制造企业管理的心脏。它集财务、人力资源、采购、生产、销售和库存等多个业务功能于一身,为企业提供了一个统一的、高效的、实时的管理平台。然而,市场上的ERP软件繁多,选择一款真正适…

pyparamvalidate 项目背景和需求分析

目录 一、前置说明1、总体目录2、本节目标 二、项目背景三、需求分析三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器,从编码到发布全过程》 2、本节目标 阐述 pyparamvalidate 项目背景和需求分析。 二、项目背景…