一文搞懂Linux内核页框回收(Page Frame Reclamation)

2023-11-14

页替换策略(Page Replacement Policy)

每当讨论页替换策略,提及最多的就是基于LRU(Least Recently Used)的算法,但严格来说这是不对的因为这些lists并不是严格按照LRU的顺序来维护的。在Linux中LRU有两个list组成,分别是active_list和inactive_list。avtive_list的目标是包含所有进程工作使用的page而inactive_list的目标是包含回收候选者的page。因为所有可回收的page都包含在这两个list中,因此任何进程的page都有可能被回收,而不是仅仅回收属于出错进程的page,因此页回收策略是全局的。

这两个队列就像是一个简单的LRU 2Q,这两个被维护的队列分别叫Am和A1。在使用LRU 2Q时,第一次被分配的page将被放入到一个名叫A1的FIFO队列。如果某个page在A1中并且被人引用,然后改page就会被放入到一个正规的LRU管理的队列Am中。这就是像使用lru_cache_add()将page放置到inactive_list(A1)中,然后使用mark_page_accessed()将page移动到active_list(Am)中。LRU 2Q中的算法描述了这两个list的size如何调节但是Linux使用了一种简单的方法:通过使用refill_inactive()函数将page从active_list的地步移动到inactive_list中来保证active_list的size是整个page cache的2/3. 下图说明了这两个list如何结构化,page如何在这两个list中移动:

2Q算法中描述两个list中预设Am是一个LRU list但是在Linux中更像是一个时钟算法,其中周期就是active list的size。当一个page到达active list的底部的时候,就会来检查reference flag会被检查,如果被置位,该page将会active list的顶部然后接着检查下一个page。如果reference bit被清除,则需要将该page移动到inactive_list

资料直通车:最新Linux内核源码资料文档+视频资料

内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

尽管Move-To-Front的启示意味着这两个列表像是在一LRU的方式在运行但是Linux替换策略和LRU还是有很多的不同,不能将其认为是一个stack算法。尽管我们可以忽略分析多程序系统的问题和每个进程使用的memory size是不同的这个事实,但是替换策略还是不能满足inclusion property是因为每个page在list中的位置取决于list的大小和上次引用的时间。这些list并没有按优先级排序因为这会使得每次对page的引用都要更新list。当被换出进程地址空间时,这两个列表几乎被忽略,因为与被换出决策相关的是page在进程虚拟地址空间中的位置而不是在page list中的位置。(这句话不太好理解)

总结下,Linux中替换算法并不是和LRU的行为一样并且被一个benchmark在实际测试中表现不错。当前仅仅有两种case在该算法下表现得比较糟糕。

first:当要被回收的候选page是匿名page时

在这种case中,Linux在线性扫描进程页表来搜索要回收的page之前,将持续检查大量的page,但是这种场景相当少。

second:单个进程中有许多文件映射的page在inactive_list中,并且经常被写入。

该进程和kswapd进程可能进入一种不断地转换这些page的循环中并把它们放入到inactive_list中但不释放任何东西。在这种case下,只有很少的page能够从active_list移动到inactive_list因为这两个列表大小比例始终没有很大变化。

Page Cache

page cache是一组数据结构其包含一些pages如有文件映射或者块设备映射或者swap的page。当前有四种最基本类型的page存在于page cache中:

  • 通过读取内存映射的文件而产生page fault的page
  • 被称作buffer page的page:从文件系统或者块设备中读取的数据块到特定的page
  • 存在于swap cache中的匿名page
  • 属于共享内存空间的page,其和匿名page的处理方式差不多。它们唯一的不同点是共享page被加入到swap cache后当它第一次被写入,它在其存储介质的空间上会立刻保留。

存在page cache的理由是减少不必要的磁盘访问。从磁盘中读取的page被存储在page hash table中,属于struct address_space.在每次访问磁盘前,都会在page cache中搜索其在磁盘中的偏移。下面的API是用来操作page cache的:

  • void add_to_page_cache(struct page * page, struct address_space * mapping, unsigned long offset)

通过调用lru_cache_add()将page加入到LRU中,然后再将page计入到inode queue和page hash table中

  • void add_to_page_cache_unique(struct page * page, struct address_space *mapping, unsigned long offset, struct page **hash)

该函数和上一个函数很相似,除了会检查该page是否已经在page cache中存在,该函数要求其调用者不能是由pagecache_lock自旋锁

  • int page_cache_read(struct file * file, unsigned long offset)

如果offset对应的page不在page cache中时,会增加一个page,必要时会通过address_space_operations->readpage从磁盘读取数据

Page Cache Hash Table

有一个需求:page cache中的page需要快速被找到。为了实现此需求,page被插入到一个page_hash_table。page->next_hash和page->pprev_hash被用来决绝冲突。(貌似kernel2.6中已经没有page_hash_table)

在kernel2.6中使用索引树来进行page存储用于快速查找。

Adding Pages to the Page Cache

从文件或者块设别中读出来的page通常会被添加到page cache从而避免更多的磁盘IO。大多数文件系统使用generic_file_read()

当作它们的file_operations->read()函数。一般来讲文件系统是通过page来执行它们的IO操作。下面就来说明下generic_file_read()是如何操作的以及它是如何将page添加到page cache。

对于普通的IO来说,generic_file_read()在调用do_generic_read()之中会先做一些基本的检查。通过find_get_page()来查询该page是否已经在page cache中存在,如果不存在,调用page_cache_alloc_cold()从cpu code list中分配page。然后再调用add_to_page_cache_lru()将page加入到page cache同时将page加入到lru list中。如果page一旦在page cache中存在,就会调用page_cache_readahead()从磁盘中读取数据。

没用从进程空间映射的匿名page将被添加到swap cache中,这将在后续的章节来讨论。匿名page在尝试将它们换出之前,它们没有address_space作为一个映射或者是一个文件偏移将它们加入到page cache中。所以这些page仍然留在LRU list中。一旦进入page cache,匿名page和file backed page的真正不同点是匿名page将swapper_space作为address_space。

共享内存的pages在下列两种case下会被加入到page cache中。

第一种case是:当page第一次从swap中获取或者第一次被分配并且第一次被引用的时候加入到page cache。即shmem_getpage()中完成。

第二种case是当swap code调用shmem_unuse()。当一个swap area正在被deactive,并且发现一个page并且在swapper_address中并且没有一个进程在使用。

Reclaiming Pages from the LRU Lists

函数shrink_cache()是替换算法的一部分,它从inactive_list获取page并决定如何将它们换出。该函数是一个大循环,从inactive_list底部最多扫描max_scan个page来释放nr_pages个page,直到inactive_list为空。

每种不同类型的page,释放的时候具体的做法不同。具体的处理顺序如下:

page被lock了且PG_launder被置位:该page在IO中被lock,因此需要跳过此page。但是如果PG_launder被置位,这就意味着该page是第二次被发现上锁了,因此最好等待IO完成然后不管它。如果一个page通过page_cache_get()被引用因此该page不会被过早地释放并调用wait_on_page()进入睡眠知道IO完成。一旦IO结束调用 page_cache_release()来减少它的引用计数。当引用计数为0,则该page可以被回收了。

page是网页且没有被任何进程映射且没有buffer且属于文件映射或者设备:因为该page属于一个文件映射或者设备映射,因此它拥有合法的writepage()函数可用:page->mapping->a_ops->writepage.PG_dirty被清空并且PG_launder被置位说明它准备IO。在调用writepage()前需要调用page_cache_get()来增加它的引用计数来。值得注意的是这种case同样适用于处于swap cache中的匿名page。该page仍然在LRU中,当它再次被发现后,如果IO完成那么就将它简单滴释放,并且page被回收。如果IO没有完成,kernel会等待IO完成。

page拥有buff:将会调用try_to_release_page()来引用它,并尝试将它释放。如果成功了,那么它是一个匿名page,它将从LRU中移除并减少它的引用计数。匿名page存在buff只有一种情况:它指向一个swap file因为该page需要写出block-sized chunk。换句话说如果该page直接有一个file映射,那么就将其简单地减少它的引用计数,如果引用计数为0,则可以直接释放了。

匿名page且被映射到多个进程:调用swap_out()

没有进程在引用的page:如果page在swap cache中,那么将从swap cache删除。如果是一个file的一部分,那么它将从page cache中删除和释放。

Shrinking all caches

shrink_caches()的作用是释放多个cache。在Linux2.6中一次遍历各个zone,然后调用shrink_zone()来回收页框。

如果有多个释放任务,这就需要给每个释放任务设定一个优先级,priority默认是DEF_PRIORITY,如果每次释放的page数达不到预期,那么就将priority从最高优先级递减1。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文搞懂Linux内核页框回收(Page Frame Reclamation) 的相关文章

  • 编写多个mysql脚本

    是否可以在复合脚本中包含其他 mysql 脚本 理想情况下 我不想为包含的脚本创建存储过程 对于较大的项目 我想分层维护几个较小的脚本 然后根据需要组合它们 但现在 我很乐意学习如何包含其他脚本 source是一个内置命令 您可以在 MyS
  • 远程linux服务器到远程linux服务器大型稀疏文件复制 - 如何?

    我有两台 CentOS 5 4 服务器 每台服务器上都安装了 VMware Server 假设我始终对 vmware 虚拟机使用稀疏文件 将虚拟机文件从一台服务器复制到另一台服务器的最可靠 最快速的方法是什么 虚拟机的文件复制起来很痛苦 因
  • 如何在 Linux x86_64 上模拟 iret

    我正在编写一个基于 Intel VT 的调试器 由于当 NMI Exiting 1 时 iret 指令在 vmx guest 中的性能发生了变化 所以我应该自己处理vmx主机中的NMI 否则 guest会出现nmi可重入错误 我查了英特尔手
  • linux下如何获取昨天和前天?

    我想在变量中获取 sysdate 1 和 sysdate 2 并回显它 我正在使用下面的查询 它将今天的日期作为输出 bin bash tm date Y d m echo tm 如何获取昨天和前天的日期 这是另一种方法 对于昨天来说 da
  • 为什么 ld 无法从 /etc/ld.so.conf 中的路径找到库?

    我想添加 opt vertica lib64进入系统库路径 所以我执行以下步骤 1 添加 opt vertica lib64 into etc ld so conf 然后运行ldconfig 2 检查 bash ldconfig p gre
  • 我应该使用哪个 Linux 发行版作为 Xen 主机? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我为家庭办公室订购了一台服务器 我想用 Xen 对其进行分区 我认为这将使事情保持干净并且更容易维护 我将运行 MySQL PostgreSQL
  • perf stat中的cycles注释是什么意思

    8 014196 task clock 0 004 CPUs utilized 204 context switches 0 025 M sec 32 cpu migrations 0 004 M sec 0 page faults 0 0
  • 是否从页面缓存中的脏页面进行文件读取?

    当字节写入文件时 内核不会立即将这些字节写入磁盘 而是将这些字节存储在页缓存中的脏页中 回写缓存 问题是 如果在脏页刷新到磁盘之前发出文件读取 则将从缓存中的脏页提供字节 还是首先将脏页刷新到磁盘 然后进行磁盘读取以提供字节 将它们存储在进
  • 进程名称长度的最大允许限制是多少?

    进程名称允许的最大长度是多少 我正在读取进程名称 proc pid stat文件 我想知道我需要的最大缓冲区 我很确定有一个可配置的限制 但就是找不到它在哪里 根据man 2 prctl http man7 org linux man pa
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • CentOS目录结构是树形的吗?

    CentOS 上有相当于树的东西吗 如果你的 Centos 系统上没有安装 tree 无论如何我通常建议服务器设置使用最小安装磁盘 你应该在命令行中输入以下内容 yum install tree y 如果没有安装 那是因为您没有正确的存储库
  • 如何在 Linux 主机上的 docker 容器中挂载目录 [重复]

    这个问题在这里已经有答案了 我想将一个目录从 docker 容器挂载到本地文件系统 该目录是网站根目录 我需要能够使用任何编辑器在本地计算机上编辑它 我知道我可以跑docker run v local path container path
  • 如何查明 Ubuntu 上安装了哪个版本的 GTK+?

    我需要确定 Ubuntu 上安装了哪个版本的 GTK 男人似乎不帮忙 这个建议 https stackoverflow com a 126145 会告诉您安装了哪个 2 0 的次要版本 不同的主要版本将具有不同的包名称 因为它们可以在系统上
  • gethostbyname() 或 getnameinfo() 如何在后台工作?

    How gethostbyname or getnameinfo 在后台工作 include
  • 如何让 clangd 转向 c++20

    当没有其他信息时 如何让 clangd 回退到 c 20 例如 在第一次构建之前 cmake 可以生成一个 这是在带有最新 LLVM 的 Arch Linux 上 这是通过 Emacs LSP 运行的 但这应该没有什么区别 你可以加 Com
  • 标准头文件中的 C 编译器错误 - 未定义的 C++ 定义

    我正在尝试编译 C 程序 但收到许多错误 这些错误是在标准 C 头文件 inttypes h stdio h stat h 等 中遇到的 错误的来源是以下未定义的常量 BEGIN DECLS END DECLS BEGIN NAMESPAC
  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • 在 Ubuntu 16.04 上找不到 printf.c

    我最近切换到Ubuntu 16 04 我在用vscode作为 Ubuntu 上的 IDE 我配置了其他语言 但我无法做到这一点C C 我创建c cpp properties json launch json tasks json 当我开始编
  • Bash 方法的返回值总是模 256

    我有一个 bash 脚本方法 它返回输入值 然而 返回值始终是模 256 的值 我用 google 搜索了一段时间 发现this http www tldp org LDP abs html exitcodes html文章说它总是以 25
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst

随机推荐

  • vmware 进入虚拟机VMware的系统后鼠标不能点

    vmware 进入虚拟机VMware的系统后鼠标不能点 1 关闭虚拟机 重启win10 再打开虚拟机好了 2
  • JAVA经典算法40题(1)

    不多说直接上题 程序1 题目 古典问题 有一对兔子 从出生后第3个月起每个月都生一对兔子 小兔子长到第四个月后每个月又生一对兔子 假如兔子都不死 问每个月的兔子总数为多少 1 程序分析 兔子的规律为数列1 1 2 3 5 8 13 21 p
  • Java中的transient关键字

    前言 说实话学了一段时间java的朋友对于transient这个关键字依旧很陌生基本没怎么用过 但是transient关键字在java中却起到了不可或缺的地位 如果要说讲到 我觉得最可能出现的地方是IO流中对象流 也叫序列化流 的时候会讲到
  • 猫和老鼠服务器维修有问题,猫和老鼠:游戏中大范围的断网掉线问题 玩家:土豆服务器就这样...

    小伙伴们大家好 我是你们的老朋友小可 欢迎大家来到猫和老鼠手游小可堂的第57期 上一期我们讲了大佬对局中都在做什么 不要忽略有些细节哦 那也是致胜的关键点 本期我们要讲的内容有 折磨人的猫 四排牛仔欢乐多和土豆服务器等 折磨人的猫 面对敌方
  • ES recovery、主副分片复制会有一段时间block写入?

    先说结论 1 ES在主副本分片复制时候不会block写入 version gt 2 x 2 ES在recovery主分片时候会有一段时间block写入 全文 ES recovery 主副分片复制会有一段时间block写入 阿里云开发者社区E
  • 023货仓选址(绝对值不等式)

    题目描述 在一条数轴上有 N 家商店 它们的坐标分别为 A1 AN 现在需要在数轴上建立一家货仓 每天清晨 从货仓到每家商店都要运送一车商品 为了提高效率 求把货仓建在何处 可以使得货仓到每家商店的距离之和最小 输入格式 第一行输入整数N
  • 数字化转型提出新要求 新华三IT基础架构如何随需而变?

    企业数字化转型转型的加速 正在给新技术带来全新机遇 在这个过程中 IT架构该如何做好支撑 不妨看看新华三怎么做的 当我们还在探讨数字化转型过程中 企业应该遵循怎样的路径时 企业的IT基础架构早已开始了云化升级 并提出了一系列全新的要求 更高
  • Python抠图:使用OpenCV实现背景去除

    一 了解抠图和OpenCV库 抠图 Matting 是图像处理领域的重要任务之一 旨在将对象与其它部分分离 OpenCV是一个开源计算机视觉库 它提供了丰富的函数和工具进行图像编辑处理 可以简单而快速地实现抠图功能 同时可以进行更多的图像处
  • 数据挖掘竞赛预测模型——五折交叉验证

    使用catboost进行五折交叉验证 import numpy as np import pandas as pd import catboost as cbt from sklearn metric import f1 score fro
  • Pycharm生成Getter和Setter的代码模板

    Setting Live Templates Python Live Template property def field self return self field field setter def field self field
  • Logstash输出到Elasticsearch笔记

    output配置中elasticsearch配置 action index 给一个文档建立索引 delete 通过id值删除一个文档 这个action需要指定一个id值 create 插入一条文档信息 如果这条文档信息在索引中已经存在 那么
  • Linux安装Node

    目录 一 下载 解压 二 配置环境变量 三 验证是否安装成功 一 下载 解压 cd opt mkdir nodejs cd nodejs wget https cdn npm taobao org dist node v12 13 0 no
  • conda 切换为国内源【清华源与中科大源】

    conda 切换为国内源 清华源与中科大源 写在前面 conda 添加清华源 conda 添加中科大源 切换默认源 End 写在前面 最近 一直在学习机器学习相关的内容 在这个过程中 需要利用Anaconda配置虚拟环境运行示例程序 但是在
  • 【电路】电容(一)——浅析大小电容的高低频滤波、并联问题

    思考 容抗计算公式 理论上 对于高频信号 明显电容越大 c 容抗更小 Xc 这样高频信号才更容易通过 一 电容等效效应 理想的电容由C构成 当然生活中不存在理想的电容 所以实际电容通常需要等效成 电容 寄生电感 ESL效应 寄生电阻 ESR
  • Pytorch之训练的完整过程(最终篇)

    先引入库 事实上是在构建时引入的 note9 train py import torchvision from torch utils tensorboard import SummaryWriter from note9 LeNet im
  • Windows10用cmd命令查看系统盘(diskpart、list)

    本文章主要总结在Windows下的cmd控制窗口 查询磁盘盘符用法 具体的查询步骤如下所述 1 1打开cmd 输入如下命令 diskpart 1 2在出现界面 继续输入如下命令 list vol 参考内容 http tieba baidu
  • 数学文化赏析期末笔记

    文章目录 一 笔记 1 各种知识点 结构 思考方法 历史 杂七杂八 x 数学之功 2 推理啥的 3 数学之辩 4 数学之美 5 数集 6 几何 7 幻方 8 难题 猜想与定理 二 典中典题目 二进制猜数游戏 斐波那契数列 派对问题 扑克牌余
  • 斯坦福博士独作!大模型训练速度再翻倍,还官宣加入明星创业公司当首席科学家...

    丰色 发自 凹非寺量子位 公众号 QbitAI 现有大语言模型的训练和推理速度 还能再快一点 快多少 2 4倍 各种大模型都在用的FlashAttention今天正式发布第2代并开源 所有Transformer架构的模型都可使用它来加速 一
  • 在线检测本机ip的网站

    https www ip cn http www cip cc https tool lu ip http www ip138 com http www net cn static customercare yourip asp
  • 一文搞懂Linux内核页框回收(Page Frame Reclamation)

    页替换策略 Page Replacement Policy 每当讨论页替换策略 提及最多的就是基于LRU Least Recently Used 的算法 但严格来说这是不对的因为这些lists并不是严格按照LRU的顺序来维护的 在Linux