Linux内存管理:(五)反向映射RMAP

文章说明:

  • Linux内核版本:5.0

  • 架构:ARM64

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

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

    zhangzihengya/LinuxSourceCode_v5.0_study (github.com)

1. 前置知识:page数据结构中的相关字段

本文主要对反向映射RMAP进行讲解,在讲解之前,我们先了解下page数据结构中与RMAP相关的几个字段:

  • mapping:表示页面所指向的地址空间。内核中的地址空间通常有两个不同的地址空间,—个用于文件映射页面,如在读取文件时,地址空间用于将文件的内容数据与装载数据的存储介质区关联起来;另—个用于匿名映射。内核使用一个简单直接的方式实现了“一个指针,两种用途”,mapping成员的最低两位用于判断是否指向匿名映射或KSM页面的地址空间。如果指向匿名页面,那么mapping成员指向匿名页面的地址空间数据结构anon_vma。
  • _refcount:表示内核中引用该页面的次数。
    • 当_refcount的值为0时,表示该页面为空闲页面或即将要被释放的页面
    • 当_refcount的值大于0时,表示该页面已经被分配且内核正在使用,暂时不会被释放
  • _mapcount:表示这个页面被进程映射的个数,即已经映射了多少个用户PTE。每个用户进 程都拥有各自独立的虚拟空间和一份独立的页表,所以可能出现多个用户进程地址空间同时映射到一个物理页面的情况,RMAP系统就是利用这个特性来实现的。_mapcount主要用于RMAP系统中。
    • 若_mapcount等于-1,表示没有PTE映射到页面
    • 若_mapcount等于0,表示只有父进程映射到页面。匿名页面刚分配时,初始化为0。

2. RMAP的背景

用户进程在使用虚拟内存的过程中,从虚拟内存页面映射到物理内存页面时,PTE保留这个记录,page数据结构中的_mapcount记录有多少个用户PTE映射到物理页面。用户PTE是指用户进程地址空间和物理页面建立映射的PTE,不包括内核地址空间映射物理页面时产生的PTE。有的页面需要迁移,有的页面长时间不使用,需要交换到磁盘。在交换之前,必须找出哪些进程使用这个页面,然后解除这些映射的用户PTE。一个物理页面可以同时被多个进程的虚拟内存映射,但是一个虚拟页面同时只能映射到一个物理页面。

在Linux 2.4内核中,为了确定某一个页面是否被某个进程映射,必须遍历每个进程的页表,因此工作量相当大,效率很低。在Linux2.5内核开发期间,提出了反问映射(Reverse Mapping,RMAP)的概念。

3. RMAP的主要数据结构

RMAP的主要目的是从物理页面的page数据结构中找到有哪些映射的用户PTE,这样页面回收模块就可以很快速和高效地把这个物理页面映射的所有用户PTE都解除并回收这个页面。

为了达到这个目的,内核在页面创建时需要建立RMAP的“钩子”,即建立相关的数据结构,RMAP系统中有两个重要的数据结构:一个是anon_vma,简称AV;另一个是anon_vma_chain,简称AVC。

anon_vma 数据结构:

// 主要用于连接物理页面的 page 数据结构和 VMA 的 vm_area_struct 数据结构
struct anon_vma {
	// 指向 anon_vma 数据结构的根节点
	struct anon_vma *root;		/* Root of this anon_vma tree */
	// 保护 anon_vma 数据结构中链表的读写信号量
	struct rw_semaphore rwsem;	/* W: modification, R: walking the list */
	// 引用计数
	atomic_t refcount;

	...

	// 指向父 anon_vma 数据结构
	struct anon_vma *parent;	/* Parent of this anon_vma */

	// 红黑树根节点。anon_vma 内部有一颗红黑树
	struct rb_root_cached rb_root;
};

在这里插入图片描述

anon_vma_chain 数据结构:

// 起枢纽的作用,比如连接父子进程间的 struct anon_vma 数据结构
struct anon_vma_chain {
	// 指向 VMA。可以指向父进程的 VMA,也可以指向子进程的 VMA,具体情况需要具体分析
	struct vm_area_struct *vma;
	// 指向 anon_vma 数据结构。可以指向父进程的 anon_vma,也可以指向子进程的 anon_vma,具体情况需要具体分析
	struct anon_vma *anon_vma;
	// 链表节点,通常把 anon_vma_chain 添加到 vma->anon_vma_chain 链表中
	struct list_head same_vma;   /* locked by mmap_sem & page_table_lock */
	// 红黑树节点,通常把 anon_vma_chain 添加到 anon_vma->rb_root 的红黑树中
	struct rb_node rb;			/* locked by anon_vma->rwsem */
	...
};

4. 父进程产生匿名页面

父进程为自己的进程地址空间VMA分配物理内存时,通常会产生匿名页面。例如:

用户态malloc()分配虚拟内存
    → 用户进程写内存
    	→ 内核发生缺页异常
    		→ do_anonymous_page()

父进程产生匿名页面时的状态如下图所示:

在这里插入图片描述

  • 父进程的每个VMA中有一个anon_vma数据结构(下文用AVp来表示),vma->anon_vma指向AVp
  • 和VMAp相关的物理页面page->mapping都指向AVp
  • 有一个anon_vma_chain数据结构,其中avc->vma指向VMAp,avc->av指向AVp
  • 把anon_vma_chain添加到VMAp->anon_vma_chain链表中
  • 把anon_vma_chain添加到AVp->anon_vma红黑树中

5. 根据父进程创建子进程

父进程通过fork()系统调用创建子进程时,子进程会复制父进程的VMA数据结构的内容,并且会复制父进程的PTE内容到子进程的页表中,实现父、子进程共享页表。多个不同子进程中的虚拟页面会同时映射到同一个物理页面。另外,多个不相干的进程的虚拟页面可以通过KSM机制映射到同—个物理页面中,这里暂时只讨论前者。为了实现RMAP系统,在子进程复制父进程的VMA时,需要添加RMAP“钩子”。

父进程通过fork调用创建子进程时,RMAP机制的流程如下图所示:

在这里插入图片描述

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

父进程fork子进程->do_fork()->copy_process()->copy_mm()->dup_mm()->dup_mmap()

// 复制父进程的地址空间
static __latent_entropy int dup_mmap(struct mm_struct *mm,
					struct mm_struct *oldmm)
{
	...
	// 遍历父进程所有的 VMA
	for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) {
		struct file *file;

		...
		// 新建一个临时用的 VMA 数据结构 tmp,复制父进程 VMA 数据结构的内容到 tmp
		tmp = vm_area_dup(mpnt);
		...
		tmp->vm_mm = mm;
		...
		// anon_vma_fork() 为子进程创建相应的 anon_vma 数据结构
		} else if (anon_vma_fork(tmp, mpnt))
		...

		// 把 tmp 添加到子进程的红黑树中
		__vma_link_rb(mm, tmp, rb_link, rb_parent);
		...
			// 复制父进程的 PTE 到子进程页表中
			retval = copy_page_range(mm, oldmm, mpnt);

		...
}

父进程fork子进程->do_fork()->copy_process()->copy_mm()->dup_mm()->dup_mmap()->anon_vma_fork

// 主要作用是把 VMA 绑定到子进程的 anon_vma 数据结构中
// 参数 vma 表示子进程的 VMA
// 参数 pvma 表示父进程的 VMA
int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
{
	...
	// 若父进程没有 anon_vma 数据结构,就不需要绑定了
	if (!pvma->anon_vma)
		return 0;

	// anon_vma_clone() 函数:
    // 遍历父进程VMA中的anon_vma_chain链表寻找anon_vma_chain实例,这里称这个实例为pavc;
    // 分配一个新的anon_vma_chain数据结构,这里称为anon_vma_chain枢纽;
    // 通过pavc找到父进程VMA中的anon_vma;
    // 把这个anon_vma_chain枢纽挂入子进程的VMA的anon_vma_chain链表中,同时把anon_vma_chain枢纽添加到属于父进程的anon_vma->rb_root的红黑树中,使子进程和父进程的VMA之间有一个联系的纽带;
	error = anon_vma_clone(vma, pvma);
	if (error)
		return error;

	// 若子进程的 VMA 已经创建了 anon_vma 数据结构,说明绑定已经完成
	if (vma->anon_vma)
		return 0;

	// 分配属于子进程的 anon_vma 和 anon_vma_chain
	anon_vma = anon_vma_alloc();
	...
	// get_anon_vma() 增加 anon_vma 数据结构中的 refcount,注意这里增加的是父进程的 anon_vma 中的引用计数
	get_anon_vma(anon_vma->root);
	...
	// 把 anon_vma_chain 挂入子进程的 vma->anon_vma_chain 链表中,同时把 anon_vma_chain 加入子进程的 anon_vma->rb_root 红黑树中
	// 至此,子进程的 VMA 和父进程的 VMA 之间的纽带建立成功
	anon_vma_chain_link(vma, avc, anon_vma);
	...
}

6. 子进程发生写时复制

如果子进程的VMA发生写时复制,那么page->mmapmg指针指向子进程VMA对应的anon_vma数据结构。在do_wp_page()函数中处理写时复制的情况。流程如下图所示:

子进程和父进程共享的匿名页面,子进程的VMA发生写时复制
    ->缺页中断发生
	  ->handle_pte_fault()
	    ->do_wp_page()
		  ->wp_page_copy()
			->分配一个新的匿名页面
			  ->page_add_new_anon_rmap()
				->__page_set_anon_rmap()使用子进程的anon_vma来设置page->mapping

子进程发生写时复制时,RMAP机制的流程如下图所示:

在这里插入图片描述

7. RMAP的应用

RMAP的典型应用场景如下:

  • kswapd内核线程为了回收页面,需要断开所有映射到该匿名页面的用户PTE
  • 页面迁移时,需要断开所有映射到匿名页面的用户PTE

RMAP的核心函数是try_to_unmap(),内核中的其他模块会调用此函数来断开一个页面的所有映射:

bool try_to_unmap(struct page *page, enum ttu_flags flags)
{
	struct rmap_walk_control rwc = {
		.rmap_one = try_to_unmap_one,
		.arg = (void *)flags,
		.done = page_mapcount_is_zero,
		.anon_lock = page_lock_anon_vma_read,
	};

	...

	if (flags & TTU_RMAP_LOCKED)
		rmap_walk_locked(page, &rwc);
	else
		rmap_walk(page, &rwc);

	// 判断 page 的 _mapcount:
	// 若 _mapcount 为 -1,说明所有映射到这个页面的用户 PTE 都已经解除完毕,因此返回 true
	// 否则返回 true
	return !page_mapcount(page) ? true : false;
}

rmap_walk_control 数据结构:

// 用于统一管理 unmap 操作
struct rmap_walk_control {
	void *arg;
	// 表示具体断开某个 VMA 上映射的 PTE
	bool (*rmap_one)(struct page *page, struct vm_area_struct *vma,
					unsigned long addr, void *arg);
	// 表示判断一个页面是否断开成功
	int (*done)(struct page *page);
	// 实现一个锁机制
	struct anon_vma *(*anon_lock)(struct page *page);
	// 表示跳过无效的 VMA
	bool (*invalid_vma)(struct vm_area_struct *vma, void *arg);
};

以匿名页面为例来介绍RMAP的应用:

try_to_unmap()->rmap_walk()->rmap_walk_anon()

// 断开一个匿名页面的所有映射
// 参数 page 表示需要解除映射的物理页面的 page 数据结构
// 参数 rwc 表示 rmap_walk_control 数据结构
// 参数 locked 表示是否已经加锁
static void rmap_walk_anon(struct page *page, struct rmap_walk_control *rwc,
		bool locked)
{
	...

	if (locked) {
		// 若 locked 已经加锁
		// 调用 page_anon_vma() 函数来获取 anon_vma 数据结构
		anon_vma = page_anon_vma(page);
		/* anon_vma disappear under us? */
		VM_BUG_ON_PAGE(!anon_vma, page);
	} else {
		// 若 locked 没有加锁
		// rmap_walk_anon_lock() 函数除了要取回 anon_vma 数据结构外,还会申请一个锁
		anon_vma = rmap_walk_anon_lock(page, rwc);
	}
	...
	// 遍历 anon_vma->rb_root 红黑树中的 anon_vma_chain,从 anon_vma_chain 中可以
	// 得到相应的 VMA,然后调用 rmap_one() 来解除用户 PTE
	anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,
			pgoff_start, pgoff_end) {
		...
	}

	...
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【unity】基于Obi的绳/杆蓝图、绳杆区别及其创建方法

绳索 是通过使用距离和弯曲约束将粒子连接起来而形成的。由于规则粒子没有方向(只有位置),因此无法模拟扭转效应(维基百科),绳子也无法保持其静止形状。然而,与杆不同的是,绳索可以被撕裂/劈开,并且可以在运行时改变其…

LeetCode(36)有效的数独 ⭐⭐

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 注…

微商城怎么弄才能开通呢?

​微商城的开通,对于许多商家来说,是进入移动电商领域的重要一步。它不仅能帮助你扩大销售渠道,还能让你更好地管理和服务你的客户。那么,微商城怎么弄才能开通呢? 1、注册微信公众号:首先,你需…

奇数码问题

title: 奇数码问题 date: 2024-01-05 11:52:04 tags: 逆序对 cstefories: 算法进阶指南 题目大意 解题思路 将二维转化为一维&#xff0c;求他的逆序对&#xff0c;如果逆序对的奇偶性相同&#xff0c;则能够实现。 代码实现 #include<iostream> #include<string.h&…

《MySQL系列-InnoDB引擎05》MySQL索引与算法

文章目录 第五章 索引与算法1 InnoDB存储引擎索引概述2 数据结构与算法2.1 二分查找法2.2 二分查找树和平衡二叉树 3 B树3.1 B树的插入操作3.2 B树的删除操作 4 B树索引4.1 聚集索引4.2 辅助索引4.3 B树索引的分裂 5 Cardinality值5.1 什么是Cardinality5.2 InnoDB存储引擎的Ca…

本地引入Element UI后导致图标显示异常

引入方式 npm 安装 推荐使用 npm 的方式安装&#xff0c;它能更好地和 webpack 打包工具配合使用。 npm i element-ui -SCDN 目前可以通过 unpkg.com/element-ui 获取到最新版本的资源&#xff0c;在页面上引入 js 和 css 文件即可开始使用。 <!-- 引入样式 --> <…

vue项目使用vue-pdf插件预览pdf文件

1、安装vue-pdf&#xff1a;npm install --save vue-pdf 2、使用 具体实现代码&#xff1a;pdfPreview.vue <template><div class"container"><pdfref"pdf":src"pdfUrl":page"currentPage":rotate"pageRotate&qu…

uniapp中的导入zzx-calendar日历的使用

需求&#xff1a; 一周的日历&#xff0c;并且可以查看当月的 &#xff0c;下个月的&#xff0c;以及可以一周一周的切换日期 借助&#xff1a;hbuilder插件市场中的zzx-calendar插件库 在父组件中引入 并注册为子组件 <template><zzx-calendar selected-change&qu…

C#编程-实现数组

实现数组 数组是相同数据类型的值得集合。例如,您可以创建存储10个整数类型值的数组。数组中的变量称为数组元素。通过使用单个名称和代表数组中元素位置的索引号来访问数组元素。数组是引用类型的数据类型。下图显示系统内存中的数组结构。 声明数组 在程序中使用数组之前需…

Java中的SPI机制

Java中的SPI&#xff08;Service Provider Interface&#xff09;机制是一种服务发现机制。它允许服务提供者在运行时被发现和加载&#xff0c;而不是在编译时。这种机制主要用于实现解耦&#xff0c;使得接口的定义与实现可以独立变化&#xff0c;增强了系统的可扩展性和可替换…

Windows下MongoDB启动及停止服务

1.CMD黑窗口输入启动命令&#xff1a; net start MongoDB 2.CMD黑窗口输入停止命令&#xff1a; net stop MongoDB

MindSpore Serving与TGI框架 の 对比

一、MindSpore Serving MindSpore Serving是一款轻量级、高性能的服务工具&#xff0c;帮助用户在生产环境中高效部署在线推理服务。 使用MindSpore完成模型训练>导出MindSpore模型&#xff0c;即可使用MindSpore Serving创建该模型的推理服务。 MindSpore Serving包含以…

基于ssm的班级事务管理系统+vue论文

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对班级事务信息管理的提升&#x…

FA2016AA (MHz范围晶体单元超小型低轮廓贴片) 汽车

随着科技的不断发展&#xff0c;智能汽车逐渐成为人们出行的首选。而其中&#xff0c;频率范围在19.2 MHz ~ 54 MHz的晶体单元超小型低轮廓贴片&#xff08;FA2016AA&#xff09;为汽车打造更智能、更舒适、更安全的出行体验。FA2016AA贴片的外形尺寸为2.0 1.6 0.5 mm&#x…

跨境电商竞品分析:洞察市场,赢得先机的关键策略

在全球化日益加速的今天&#xff0c;跨境电商已经成为了企业拓展市场、提高销售额的重要手段。然而&#xff0c;跨境电商市场的竞争也日趋激烈&#xff0c;如何在众多竞争对手中脱颖而出&#xff0c;成为每个企业都面临的挑战&#xff1b;想要做到这点&#xff0c;了解竞品情况…

电脑提示找不到mfc140u.dll,无法继续执行代怎么办,mfc140u.dll丢失的解决办法

在使用电脑时&#xff0c;我们常常会遇到各种各样的问题。其中一个比较常见的问题就是“找不到mfc140u.dll,无法继续执行代码”。今天小编主要就围绕这mfc140u.dll这个文件来给大家详细的解析一下吧&#xff0c;让大家更清楚的知道这个问题以及怎么去解决这个问题。接下来一起来…

第10课 利用windows API捕获桌面图像并通过FFmpeg分享

在上一章&#xff0c;我们已经实现了一对一音视频对话功能。在实际应用中&#xff0c;我们常需要把自己的电脑桌面分享给他人以实现桌面共享功能&#xff0c;这种功能在视频会议、在线教学等场景中很常见&#xff0c;这种功能如何实现呢&#xff1f;这节课我们就来解决这个问题…

【响应式编程-03】常见的函数式接口

一、简要描述 使用Lambda的前提 必须有一个函数式接口: 有且只有一个抽象方法的接口 FunctionnalInterface注解 常见的函数式接口 Runnable / CallableSupplier / ConsumerComparatorPredicateFunction 二、代码实现 1、Runnable - RunnableLambda测试类 package tech.flygo.…

CloudCanal x Redis 数据同步指令集丰富与细节优化

简述 CloudCanal 前一段时间支持了 Redis 到 Redis 数据迁移同步能力&#xff0c;并支持其双向同步&#xff0c;但是支持的指令种类有限。 随着用户使用&#xff0c;指令支持不全面成为一个比较大的问题&#xff0c;所以最近的版本&#xff0c;我们对此能力&#xff0c;结合用…

695岛屿最大面积

题目 给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向相连&#xff08;对角线方向不相连&#xff09;。整个网格被水完全包围&#xff0c;但其中恰好有一个…