图解Linux内核(基于6.x):解读Linux内存反向映射之匿名映射

news2024/11/16 1:27:41

文章目录

  • 📑前言
  • 一、匿名映射的mapping
  • 二、推荐阅读
    • 2.1 一图速览
    • 2.2 内容简介

image.png

📑前言

内存映射中,我们经常讨论的是由虚拟内存定位物理内存(也就是folio或者page),实际上在很多场景中(比如内存回收),会涉及反向的操作,也就是反向映射。所谓反向映射,就是给定一个folio(page),将映射它的PTE(页表项)找出来。接下来我们来详细分析一下它的原理吧(本文仅分析匿名映射部分)。

一、匿名映射的mapping

匿名映射中,mapping可以用来找到anon_vma,anon_vma关联vma,通过folio和vma,就可以得出映射的虚拟地址address,最终由address和vma定位PTE,如图1所示。


图1.匿名folio定位address示意图
图1中基本都是直来直去的关系,除了anon_vma和vma,它们实际上是多对多的关系,由anon_vma_chain结构体(以下简称avc)辅助实现。
我们从mmap返回,vma还没有映射任何物理页的情景说起。
第一次访问vma区间的地址,导致缺页异常。内核调用do_anonymous_page,申请一页内存,完成映射。
由于这是vma区间内的第一次缺页异常,vma相关的anon_vma和avc还不存在,处理异常的过程中会准备好它们,然后调用page_add_new_anon_rmap为该page(folio)建立反向映射,将anon_vma赋值给mapping字段。关键代码片段如下。

struct anon_vma *anon_vma = vma->anon_vma;

anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON; 
WRITE_ONCE(page->mapping, (struct address_space *) anon_vma); 
page->index = linear_page_index(vma, address);

代码中的address就是映射的虚拟地址,page->index实际上是page offset,该page在文件中的偏移量,也就是映射的是文件的第几页,计算代码如下。

pgoff = (address - vma->vm_start) >> PAGE_SHIFT; 
pgoff += vma->vm_pgoff;  
return pgoff;

vma->vm_pgoff是vma的起始地址对应的文件的page offset。
匿名映射没有对应文件,它的vma->vm_pgoff等于vma->vm_start >> PAGE_SHIFT。
这里需要明确一下,从内核的角度看,我们以MAP_ANONYMOUS调用mmap等完成的映射并不一定是匿名映射。置位MAP_SHARED的情况下,内核会生成“假”(pseudo)文件与之对应(shmem_zero_setup),就不是匿名的了,vma->vm_pgoff等于0。只有MAP_ANONYMOUS和 MAP_PRIVATE同时置位的情况下才是内核承认的匿名映射。
这里有以下两点需要注意。

  1. 整个vma可能会有多个页,它们的mapping字段是相等的,不等的是index字段。
  2. anon_vma和vma的关系并不依赖page,哪怕是vma映射中的其中一部分page改变映射了,从anon_vma到vma的路径并不会变。

单个进程的反向映射建立了,如图2所示。anon_vma到vma实际上是通过区间树(interval tree)实现的,为了看起来简洁些图中使用链表代替。


图2.匿名映射单个进程反向映射示意图
接下来考虑创建子进程的场景。在新进程创建的过程中,有些情况会调用dup_mmap复制原进程的内存空间,dup_mmap会复制vma,然后调用anon_vma_fork。anon_vma_fork会为新进程申请anon_vma,建立反向映射,完成后如图3所示。

图3.创建子进程后匿名映射示意图
新进程创建完成后,从page->mapping出发,可以遍历所有映射它的PTE了。
再考虑COW的场景,缺页异常申请新的一页,将原页的内存复制到新页中,然后使用新页更新映射,根据前文中“需要注意的第2点”可以得出图4中的结果。

图4.COW发生后匿名映射示意图
可以看到,从原页依然可以遍历到没有映射它的vma(请仔细理解anon_vma和vma的关系并不依赖page),从新页出发倒是没有这个烦恼。
我们肯定不希望操作原页的时候会影响到没有映射它的vma,所以得到某个vma后,需要做进一步检查,原理是拿原页的pfn区间(一个folio可能包含多个连续的物理页)和vma映射的物理页的pfn做比较,落在区间内才是有效的,由check_pte实现。
有了以上的铺垫,我们可以分析匿名页的反向映射了,由rmap_walk_anon实现,核心逻辑如下。

void rmap_walk_anon(struct folio *folio,  struct rmap_walk_control *rwc, bool locked)
{  
    struct anon_vma *anon_vma;  
    pgoff_t pgoff_start, pgoff_end;  
    struct anon_vma_chain *avc;
    if (locked) 
    {    
        anon_vma = folio_anon_vma(folio);    //1  
    } 
    else 
    {    
        anon_vma = rmap_walk_anon_lock(folio, rwc);  
    }
    
    pgoff_start = folio_pgoff(folio);    //2  
    pgoff_end = pgoff_start + folio_nr_pages(folio) - 1;  
    anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,pgoff_start, pgoff_end) 
    {
        struct vm_area_struct *vma = avc->vma;
        unsigned long address = vma_address(&folio->page, vma);    //3
        if (rwc->invalid_vma && rwc->invalid_vma(vma, rwc->arg))    //4      
            continue;    
        if (!rwc->rmap_one(folio, vma, address, rwc->arg))      
            break;    
        if (rwc->done && rwc->done(folio))      
            break;  
    }
    if (!locked)    
        anon_vma_unlock_read(anon_vma);
}

第1步,获得anon_vma,是给anon_vma->mapping赋值(见前文代码片段)的反过程。
第2步,调用folio_pgoff得到pgoff_start,然后根据folio的页数得到pgoff_end,用作遍历interval tree的时候筛选vma。folio_pgoff返回folio->index,赋值过程也见前文代码片段。
第3步,根据folio和vma计算得到虚拟地址,不考虑多页的情况下,计算过程如下。

pgoff_in_vma = page->index - vma->vm_pgoff
address = vma->vm_start + (pgoff_in_vma << PAGE_SHIFT)

这个计算过程对匿名映射和文件映射都适用。vma->vm_pgoff是vma基于文件的page offset,vma->vm_start是vma区间的其实虚拟地址,加上当前页在vma内的offset就可以得到虚拟地址了。匿名映射没有文件,vma->vm_pgoff等于vma->vm_start >> PAGE_SHIFT,用来做计算也是没有问题的。
这里anon_vma_interval_tree_foreach会筛选树上符合pgoff_start, pgoff_end区间的vma,难道anon_vma上的vma可以有不同的pgoff区间吗?答案是肯定的,为了简化问题,我们之前回避了anon_vma的重复利用问题,同一个进程符合条件的vma是可以共享anon_vma的(find_mergeable_anon_vma)。从这个角度看,vma->vm_pgoff等于vma->vm_start >> PAGE_SHIFT是合理的,同一个进程不同的vma计算得到的vma->vm_pgoff也不同。
第4步,调用rmap_walk_control(代码中简称rwc)提供的回调函数。rmap_walk_anon提供了遍历vma的方法,至于对每个vma做什么,是由调用它的函数决定的,比如folio_referenced函数希望遍历PTE,查看folio被不同PTE访问的次数,它的rwc定义如下。

struct folio_referenced_arg pra = {
    .mapcount = folio_mapcount(folio),
    .memcg = memcg,
};
struct rmap_walk_control rwc = {
    .rmap_one = folio_referenced_one,
    .arg = (void *)&pra,
    .anon_lock = folio_lock_anon_vma_read,
    .try_lock = true,
};

另外,rmap_walk_anon给出了vma、address和folio,但没有得到PTE,这个任务只能由rwc的回调函数自行完成,不过内核提供了page_vma_mapped_walk函数辅助完成该任务。

二、推荐阅读

2.1 一图速览


《图解Linux内核(基于6.x)》
京东:https://item.jd.com/14577130.html

2.2 内容简介

  • 全书共五篇,以从易到难的顺序详细剖析了Linux内核开发的核心技术。“知识储备篇”介绍了Linux的数据结构、中断处理、内核同步和时间计算等内容,这些是理解后续章节的前提;之后通过“内存管理篇”“文件系统篇”“进程管理篇”详细介绍了Linux的三大核心模块;最后的“综合应用篇”则融合了前面诸多模块知识展示了Linux内核开发在操作系统、智能设备、驱动、通信、芯片、云计算和人工智能等热点领域的应用。书中的重点、难点均配有图表、代码和实战案例,力求直观、清晰。
  • 学习本书的读者需要熟悉C语言,建议对Linux内核有一定了解。推荐初学者按照本书的编排顺序阅读,而熟悉Linux内核的读者可以跳过知识储备篇,直接从三大核心模块篇进行阅读。

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

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

相关文章

在Ubuntu中创建Ruby on Rails项目并搭建数据库

新建Rails项目 先安装bundle Ruby gem依赖项工具&#xff1a; sudo apt install bundle 安装Node.js: sudo apt install nodejs 安装npm 包管理器&#xff1a; sudo apt install npm 安装yarn JavaScript包管理工具&#xff1a; sudo apt install yarn 安装webpacker: …

微信小程序毕业设计-电影院订票选座系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

基于riscv架构的DAYU800开发板套件介绍

一、简介 润和-SCDAYU800 开发平台基于平头哥高性能 RISC-V 开源架构曳影 TH1520 芯片&#xff0c;集成4核高性能RISC-V处理器玄铁C910的平头哥曳影1520&#xff0c;AI算力达4TOPs支持蓝牙、音频、视频和摄像头等功能,支持多种视频输入输出接口,并提供丰富的扩展接口&#xff…

即时到账支付系统源码第四方支付平台源码(支付宝/QQ钱包/微信二维码收款+附配套软件)

即时到账支付系统源码第四方支付平台源码价值10万&#xff0c;支付宝/QQ钱包/微信二维码收款&#xff0c;附配套软件 开发语言&#xff1a;phpmysql 这个是一个可以跟码支付一样用自己的二维码收款的网站 还可以作为即时到账 代收款 或者易支付使用后台配置好就行&#xff…

Java基础 - 练习(三)打印空心菱形

Java基础练习 打印空心菱形&#xff0c;先上代码&#xff1a; public static void diamond() {//控制行数for (int i 1; i < 4; i) {//空格的个数for (int k 1; k < 4 - i; k) {System.out.print(" ");}//控制星星个数的时候和行有关for (int j 1; j <…

网络层 IP协议【计算机网络】【协议格式 || 分片 || 网段划分 || 子网掩码】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;前提 二&…

3.什么是计算机语言

什么是计算机语言 ? 计算机语言&#xff08;Computer Language&#xff09;指用于人与计算机之间通讯的语言。计算机语言是人与计算机之间传递信息的媒介。计算机系统最大特征是指令通过一种语言传达给机器。为了使电子计算机进行各种工作&#xff0c;就需要有一套用以编写计…

面向对象复习(java)

文章目录 包在本地 cmd 编译包规则import(导包&#xff09; thisthis 访问实例方法this 访问构造方法 supersuper 访问父类构造器super访问父类方法super 访问父类属性 构造方法访问权限封装继承细节方法重写(覆盖)子父类同名变量问题关于子父类方法的继承问题 多态向上转型和向…

怎么把webp文件转换为jpg?快来试试这四种转换方法!

怎么把webp文件转换为jpg&#xff1f;Webp是一种不常见的图片格式&#xff0c;这种格式在使用过程中有很多缺点&#xff0c;首先它的浏览器兼容性不是很强&#xff0c;这就代表大家无法随意进行网络传输&#xff0c;可能需要准备特定的操作才能进行&#xff0c;然后编辑webp的工…

513、找二叉树左下角的值

题解&#xff1a;层序遍历简单&#xff0c;此篇记录递归法&#xff0c;要注意左下角的值并不一定是左叶子节点&#xff0c;遍历思路形象化就是按先左后右的顺序遍历每一条分支&#xff0c;若遍历到叶子结点&#xff0c;看此时深度有没有超过之前的值&#xff0c;超过了就记录下…

森林之下延迟高如何处理 森林之下联机卡顿的解决方法

森林之下是一款结合了农场模拟、恐怖生存的游戏&#xff0c;玩家需要管理一个被“闹鬼的树林”包围的农场&#xff0c;种植农作物&#xff0c;拯救、驯服、饲养动物&#xff0c;探索被诅咒的森林&#xff0c;并且收集物品来破除诅咒。这款游戏目前已经开放了demo&#xff0c;不…

韩顺平0基础学java——第26天

p523-547 HashSet扩容时&#xff0c;只要节点到达了阈值就会扩&#xff0c;而不是数组长度到了才扩。 比如长16的数组&#xff0c;索引1放了8个&#xff0c;索引3放了4个&#xff0c;我再加一个他就会扩容。 另外谁能告诉我老师的debug界面是怎么设置的吗忘光了 HashSet存放…

《合金弹头:觉醒》横空出世,腾讯天美工作室携手SNK再塑经典

原标题&#xff1a;腾讯携手SNK发布游戏新作《合金弹头&#xff1a;觉醒》7月18日正式发布 易采游戏网6月19日消息&#xff1a;游戏界迎来一则激动人心的消息&#xff0c;由SNK正版授权&#xff0c;腾讯天美工作室研发的横版动作射击手游《合金弹头&#xff1a;觉醒》正式登陆S…

驾校在线考试系统源码 手机+PC+平板自适应

Thinkphp在线考题源码 驾校在线考试系统 手机PC平板 自适应&#xff0c;机动车驾驶培训学校驾校类网站源码带手机端 运行环境&#xff1a;phpmysql 内附安装说明 驾校在线考试系统源码 手机PC平板自适应

浅谈目标检测之YOLO(You Only Look Once)v1

简介&#xff1a;本文章要介绍的YOLOv1算法&#xff0c;它与之前的目标检测算法如R-CNN等不同&#xff0c;R-NN等目标检测算法是一种两阶段&#xff08;two-stage&#xff09;算法&#xff0c;步骤为先在图片上生成候选框&#xff0c;然后利用分类器对这些候选框进行逐一的判断…

shell中的流程控制

条件判断在流程控制中的重要性 有了条件判断才能进行if判断即分支流程&#xff0c;才能进行case的多分支流程&#xff0c;才能进行for循环和while循环。 单分支流程判断 如上图所示&#xff0c;在shell编程中常使用英文状态下的分号来在Linux控制台一次性执行多条命令&#x…

FPGA中复位电路的设计

复位电路也是数字逻辑设计中常用的电路&#xff0c;不管是 FPGA 还是 ASIC 设计&#xff0c;都会涉及到复位&#xff0c;一般 FPGA或者 ASIC 的复位需要我们自己设计复位方案。复位指的是将寄存器恢复到默认值。一般复位功能包括同步复位和异步复位。复位一般由硬件开关触发引起…

把Deepin塞进U盘,即插即用!Deepin To Go来袭

前言 小伙伴之前在某篇文章下留言说&#xff1a;把Deepin塞进U盘的教程。 这不就来了吗&#xff1f; 事实是可以的。这时候你要先做点小准备&#xff1a; 一个大小为8GB或以上的普通U盘 一个至少64GB或以上的高速U盘 一个Deepin系统镜像文件 普通U盘的大概介绍&#xff1…

Xtuner微调

环境安装 studio-conda xtuner0.1.17 conda activate xtuner0.1.17 进入家目录 &#xff08;~的意思是 “当前用户的home路径”&#xff09; cd ~ 创建版本文件夹并进入&#xff0c;以跟随本教程 mkdir -p /root/xtuner0117 && cd /root/xtuner0117 拉取 0.1.17 的版…

海外盲盒APP开发,盲盒出海热!

当下&#xff0c;盲盒作为一种热门的娱乐休闲模式&#xff0c;在全球消费市场中都非常火热&#xff0c;各种热门盲盒商品刚一上线就受到了秒杀&#xff0c;受到了各地年轻消费者的追捧&#xff01; 盲盒全球化对于我国盲盒企业来说是一个新的机会&#xff0c;有助于我国盲盒快…