QEMU源码全解析 —— 内存虚拟化(4)

接前一篇文章:

本文内容参考:

《趣谈Linux操作系统》 —— 刘超,极客时间

《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

特此致谢!

QEMU内存初始化

1. 基本结构

上一回对于QEMU中与内存相关的第二个数据结构MemoryRegion结构进行了深入讲解。本回讲解第三个数据结构RAMBlock。

(3)RAMBlock

内存管理最基础的一部分自然是物理Memory内存,然后还包括MMIO空间、IO端口的地址空间。RAMBlock结构表示的是内存条,一个RAMBlock对应虚拟机中的一个内存条。RAMBlock结构的定义在include/qemu/typedefs.h中,代码如下:

typedef struct RAMBlock RAMBlock;

struct RAMBlock的定义在include/exec/ramblock.h中,代码如下:

struct RAMBlock {struct rcu_head rcu;struct MemoryRegion *mr;uint8_t *host;uint8_t *colo_cache; /* For colo, VM's ram cache */ram_addr_t offset;ram_addr_t used_length;ram_addr_t max_length;void (*resized)(const char*, uint64_t length, void *host);uint32_t flags;/* Protected by iothread lock.  */char idstr[256];/* RCU-enabled, writes protected by the ramlist lock */QLIST_ENTRY(RAMBlock) next;QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;int fd;uint64_t fd_offset;size_t page_size;/* dirty bitmap used during migration */unsigned long *bmap;/* bitmap of already received pages in postcopy */unsigned long *receivedmap;/** bitmap to track already cleared dirty bitmap.  When the bit is* set, it means the corresponding memory chunk needs a log-clear.* Set this up to non-NULL to enable the capability to postpone* and split clearing of dirty bitmap on the remote node (e.g.,* KVM).  The bitmap will be set only when doing global sync.** It is only used during src side of ram migration, and it is* protected by the global ram_state.bitmap_mutex.** NOTE: this bitmap is different comparing to the other bitmaps* in that one bit can represent multiple guest pages (which is* decided by the `clear_bmap_shift' variable below).  On* destination side, this should always be NULL, and the variable* `clear_bmap_shift' is meaningless.*/unsigned long *clear_bmap;uint8_t clear_bmap_shift;/** RAM block length that corresponds to the used_length on the migration* source (after RAM block sizes were synchronized). Especially, after* starting to run the guest, used_length and postcopy_length can differ.* Used to register/unregister uffd handlers and as the size of the received* bitmap. Receiving any page beyond this length will bail out, as it* could not have been valid on the source.*/ram_addr_t postcopy_length;
};

上边已提到,RAMBlock结构表示的是虚拟机中的内存条,一个RAMBlock对应虚拟机中的一个内存条。RAMBlock里面记录了该内存条的一些基本信息,如所属的mr(struct MemoryRegion *mr)、如果有文件作为后端,该文件对应的fd(int fd)、系统的页面大小page_size(size_t page_size)、已经使用的大小used_length(ram_addr_t used_length)、该内存条在虚拟机整个内存中的偏移offset(ram_addr_t offset)等。

每个MemoryRegion里都包含一个RAMBlock的指针,但不一定会对应一个RAMBlock。对于物理内存,则其实体MemoryRegion会指向一个实体RAMBlock。回顾一下MemoryRegion结构中的RAMBlock的相关成员,在include/exec/memory.h中,如下:

/** MemoryRegion:** A struct representing a memory region.*/
struct MemoryRegion {Object parent_obj;/* private: *//* The following fields should fit in a cache line */bool romd_mode;bool ram;bool subpage;bool readonly; /* For RAM regions */bool nonvolatile;bool rom_device;bool flush_coalesced_mmio;uint8_t dirty_log_mask;bool is_iommu;RAMBlock *ram_block;……
};
  • RAMBlock *ram_block

ram_block表示实际分配的物理内存。

回到struct RAMBlock的定义。其中的主线逻辑变量offset(ram_addr_t offset)(GPA)和host(uint8_t *host)(HVA)。还有bmap(unsigned long *bmap)和receivedmap(unsigned long *receivedmap)是热迁移存储脏页使用。

此外,所有的RAMBlock会通过next(QLIST_ENTRY(RAMBlock) next)域连接到一个链表中,链表头是ram_list.blocks全局变量。

这里顺便提一下struct RAMBlock中ram_addr_t类型的定义。ram_addr_t的定义在include/exec/cpu-common.h中,代码如下:

/* address in the RAM (different from a physical address) */
#if defined(CONFIG_XEN_BACKEND)
typedef uint64_t ram_addr_t;
#  define RAM_ADDR_MAX UINT64_MAX
#  define RAM_ADDR_FMT "%" PRIx64
#else
typedef uintptr_t ram_addr_t;
#  define RAM_ADDR_MAX UINTPTR_MAX
#  define RAM_ADDR_FMT "%" PRIxPTR
#endif

uint64_t和uintptr_t都在中定义,分别如下:

#if __riscv_xlen == 64
typedef long			s64;
typedef unsigned long		u64;
typedef long			int64_t;
typedef unsigned long		uint64_t;
#define PRILX			"016lx"
#elif __riscv_xlen == 32
typedef long long		s64;
typedef unsigned long long	u64;
typedef long long		int64_t;
typedef unsigned long long	uint64_t;
#define PRILX			"08lx"
#else
#error "Unexpected __riscv_xlen"
#endif
typedef unsigned long		uintptr_t;

至此,QEMU中与内存相关的三个基本结构struct AddrSpace、struct MemoryRegion、struct RAMBlock就讲解完了。

再来回顾和复习一下这三个基本数据结构:

  • AddressSpace(struct AddressSpace)

AddressSpace结构用来表示一个虚拟机或者虚拟CPU能够访问的所有物理地址。struct AddressSpace的定义在include/exec/memory.h中,如下:

/*** struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects*/
struct AddressSpace {/* private: */struct rcu_head rcu;char *name;MemoryRegion *root;/* Accessed via RCU.  */struct FlatView *current_map;int ioeventfd_nb;struct MemoryRegionIoeventfd *ioeventfds;QTAILQ_HEAD(, MemoryListener) listeners;QTAILQ_ENTRY(AddressSpace) address_spaces_link;
};
  • MemoryRegion(struct MemoryRegion)

MemoryRegion表示的是虚拟机中的一段内存区域。MemoryRegion是内存模拟中的核心结构,整个内存的模拟都是通过MemoryRegion构成的无环图完成的。图的叶子节点是实际分配给虚拟机的物理内存或者MMIO,中间节点则表示内存总线,内存控制是其它MemoryRegion的别名。

struct MemoryRegion的定义也在include/exec/memory.h中,代码如下:

/** MemoryRegion:** A struct representing a memory region.*/
struct MemoryRegion {Object parent_obj;/* private: *//* The following fields should fit in a cache line */bool romd_mode;bool ram;bool subpage;bool readonly; /* For RAM regions */bool nonvolatile;bool rom_device;bool flush_coalesced_mmio;uint8_t dirty_log_mask;bool is_iommu;RAMBlock *ram_block;Object *owner;/* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */DeviceState *dev;const MemoryRegionOps *ops;void *opaque;MemoryRegion *container;int mapped_via_alias; /* Mapped via an alias, container might be NULL */Int128 size;hwaddr addr;void (*destructor)(MemoryRegion *mr);uint64_t align;bool terminates;bool ram_device;bool enabled;bool warning_printed; /* For reservations */uint8_t vga_logging_count;MemoryRegion *alias;hwaddr alias_offset;int32_t priority;QTAILQ_HEAD(, MemoryRegion) subregions;QTAILQ_ENTRY(MemoryRegion) subregions_link;QTAILQ_HEAD(, CoalescedMemoryRange) coalesced;const char *name;unsigned ioeventfd_nb;MemoryRegionIoeventfd *ioeventfds;RamDiscardManager *rdm; /* Only for RAM *//* For devices designed to perform re-entrant IO into their own IO MRs */bool disable_reentrancy_guard;
};
  • RAMBlock(struct RAMBlock)

RAMBlock结构表示的是虚拟机中的内存条,一个RAMBlock对应虚拟机中的一个内存条。RAMBlock里面记录了该内存条的一些基本信息。struct RAMBlock的定义在include/exec/ramblock.h中,如下:

struct RAMBlock {struct rcu_head rcu;struct MemoryRegion *mr;uint8_t *host;uint8_t *colo_cache; /* For colo, VM's ram cache */ram_addr_t offset;ram_addr_t used_length;ram_addr_t max_length;void (*resized)(const char*, uint64_t length, void *host);uint32_t flags;/* Protected by iothread lock.  */char idstr[256];/* RCU-enabled, writes protected by the ramlist lock */QLIST_ENTRY(RAMBlock) next;QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;int fd;uint64_t fd_offset;size_t page_size;/* dirty bitmap used during migration */unsigned long *bmap;/* bitmap of already received pages in postcopy */unsigned long *receivedmap;/** bitmap to track already cleared dirty bitmap.  When the bit is* set, it means the corresponding memory chunk needs a log-clear.* Set this up to non-NULL to enable the capability to postpone* and split clearing of dirty bitmap on the remote node (e.g.,* KVM).  The bitmap will be set only when doing global sync.** It is only used during src side of ram migration, and it is* protected by the global ram_state.bitmap_mutex.** NOTE: this bitmap is different comparing to the other bitmaps* in that one bit can represent multiple guest pages (which is* decided by the `clear_bmap_shift' variable below).  On* destination side, this should always be NULL, and the variable* `clear_bmap_shift' is meaningless.*/unsigned long *clear_bmap;uint8_t clear_bmap_shift;/** RAM block length that corresponds to the used_length on the migration* source (after RAM block sizes were synchronized). Especially, after* starting to run the guest, used_length and postcopy_length can differ.* Used to register/unregister uffd handlers and as the size of the received* bitmap. Receiving any page beyond this length will bail out, as it* could not have been valid on the source.*/ram_addr_t postcopy_length;
};

基础已经打好,下一回开始讲解其中更为详细的内容。

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

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

相关文章

el-date-picker 选择年后输出的是Wed Jan 01 2025 00:00:00 GMT+0800 (中国标准时间)

文章目录 问题分析 问题 在使用 el-date-picker 做只选择年份的控制器时,出现如下问题:el-date-picker选择年后输出的是Wed Jan 01 2025 00:00:00 GMT0800 (中国标准时间),输出了两次如下 分析 在 el-date-picker 中,我们使用…

代码随想录第三十四天

第八章 贪心算法 part04 ​ ● 860.柠檬水找零 ​ ● 406.根据身高重建队列 ​ ● 452. 用最少数量的箭引爆气球 详细布置 860.柠檬水找零 本题看上好像挺难,其实挺简单的,大家先尝试自己做一做。 https://programmercarl.com/0860.%E6%9F%A0%E6%…

数学建模【非线性规划】

一、非线性规划简介 通过分析问题判断是用线性规划还是非线性规划 线性规划:模型中所有的变量都是一次方非线性规划:模型中至少一个变量是非线性 非线性规划在形式上与线性规划非常类似,但在数学上求解却困难很多 线性规划有通用的求解准…

计算机网络之网络安全

文章目录 1. 网络安全概述1.1 安全威胁1.1.1 被动攻击1.1.2 主动攻击 1.2 安全服务 2. 密码学与保密性2.1 密码学相关基本概念2.2 对称密钥密码体制2.2.1 DES的加密方法2.2.2.三重DES 2.3 公钥密码体制 3. 报文完整性与鉴别3.1 报文摘要和报文鉴别码3.1.1 报文摘要和报文鉴别码…

Jenkins面试系列

1. Jenkins 是什么? Jenkins是一个开源的、可扩展的持续集成、交付、部署(软件/代码的编译、打包、部署)的基于web界面的平台。允许持续集成和持续交付项目,无论用的是什么平台,可以处理任何类型的构建或持续集成。 2. 为什么使用 Jenkins? Jenkins是一种使用Java编程语…

Vim相关配置

记录一下有关vim的一些设置,以免电脑寄了不好重新配置 vscodevim 首先是vscode中的vim模式 在应用商店中搜索vim插件安装即可 然后在setting中添加以下有关vim 的配置 "vim.easymotion": true,"vim.surround": true,"vim.incsearch"…

从零开始手写mmo游戏从框架到爆炸(十二)— 角色设定

导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客 写了这么多的框架,说好的mmo游戏呢?所以我们暂时按下框架不表,这几篇我们设计英雄角色、怪物、技能和地图。本篇我们来对游戏角色…

【BUG】段错误

1. 问题 8核工程,核4在运行了20分钟以上,发生了段错误。 [C66xx_4] A00x53 A10x53 A20x4 A30x167e A40x1600 A50x850e2e A60x845097 A70xbad9f5e0 A80x0 A90x33 A100x53535353 A110x0 A120x0 A130x0 A140x0 A150x0 A160x36312e35 A170x20 A180x844df0 …

没有PFMEA分析的检测过程会有什么风险?

随着科技的快速发展,产品复杂度不断提升,检测过程的重要性日益凸显。然而,在这个过程中,如果没有进行PFMEA分析,将会带来怎样的风险呢?本文将对此进行深入探讨。 众所周知,检测是确保产品质量的…

openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优

文章目录 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优222.1 前提条件222.2 内存相关参数设置222.3 网络相关参数设置222.4 I/O相关参数设置 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优 在性能调优过程中,可以根据…

数据库第六次实验

目录 1 实体完整性 1.1 单属性 1.2 多属性 2 参照完整性 2.1 单属性 2.2 多属性 3 用户自定义完整性 3.1 属性上的约束 3.2 元组上的约束 1 实体完整性 1.1 单属性 ①定义 use 实体完整性_单属性; create table Student_s_d( Sno char(9) primary key, Sna…

事务管理 及 AOP

一、事务管理 1.1 事务回顾 1.2 Spring事务管理 1.3 事务进阶 1.3.1 rollbackfor 1.3.2 propagation 控制台日志过滤插件: 查看事务管理日志是JdbcTrsactionManager类: 在控制台找到JdbcTrsactionManager——右击——add highlight——红色——所有事…

超声波清洗机洗眼镜好吗?超声波清洗机哪个品牌更值得推荐一些

随着科技的进步,很多朋友因为长时间沉迷于看电子产品,所以早早的就佩戴上眼镜了,从而离不开眼镜。眼镜长时间佩戴会导致上面积累着非常多的灰尘,堆积在镜片上就会导致视线变得模糊不清了,影响视线。然而很多人也很少去…

龙年新目标!龙蜥安全联盟第三次月会圆满结束

2024 年 2 月 1 日,龙蜥社区安全联盟(OASA,以下简称“联盟”)月度会议召开,线上线下共计 33 位代表参会,由秘书处成员齐增田主持本次会议。本次会议主要内容包括 2023 联盟回顾、2024 年的目标和规划、联盟…

【深度优先搜索】【图论】【树】2646. 最小化旅行的价格总和

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 涉及知识点 深度优先搜索 图论 树 LeetCode2646. 最小化旅行的价格总和 现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号。给你一个整数 n 和一个长…

2024.2.17 模拟实现 RabbitMQ —— 内存数据管理

目录 需求分析 内存管理 实现 MemoryDataCenter 类 封装交换机操作 封装队列操作 封装绑定操作 封装消息操作 封装未确认消息操作 封装恢复数据操作 关于线程安全 针对 MemoryDataCenter 单元测试 需求分析 当前我们已经实现了 数据库管理 交换机、绑定、队列&#…

CSS的伪类选择器:nth-child()

CSS的伪类选择器:nth-child() CSS的伪类选择器 :nth-child() 是一个非常强大的工具,它允许你根据元素在其父元素中的位置(序数)来选择特定的子元素。这个选择器可以应用于任何元素,并且可以与类型选择器、类选择器或ID选择器结合…

Java业务开发常见错误100例【1-30】

1、Tomcat的工作线程是基于线程池的,线程池会重用固定的几个线程,一旦线程重用,那么很可能首次从ThreadLocal获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal中的用户信息就是其他用户的信息。使用类似ThreadLocal工具来…

opencv源码编译及配置完整版教程(win10+vs2019+opencv-4.4.0+opencv_contrib-4.4.0)收藏

opencv源码编译及配置完整版教程(win10vs2019opencv-4.4.0opencv_contrib-4.4.0) https://blog.csdn.net/zhoufm260613/article/details/126107994

使用Python生成二维码的完整指南

无边落木萧萧下,不如跟着可莉一起游~ 可莉将这篇博客收录在了:《Python》 可莉推荐的优质博主首页:Kevin ’ s blog 本文将介绍如何使用Python中的qrcode库来生成二维码。通过简单的代码示例和详细解释,读者将学习如何在Python中轻…