qemu中的内存管理
qemu负责模拟虚机的外设,因此虚机的线性地址空间主要由qemu进行管理,也就是确定线性地址空间中哪段地址属于哪个设备或者DRAM或者其他的什么。
1、数据结构

1、RAMBLOCK
(最直接接触host内存,有hva)
RAMBLOCK才是真正分配了host内存的地方,如果把它直接理解成一个内存条也是非常合适的,但实际上不仅仅如此,还有设备自有内存,显存。ram_list则是RAMBlock的链表。
每个RAMBLOCK都有一个唯一的MemoryRegion对应,但不是每个MemoryRegion都有RAMBLOCK对应的。
typedef struct RAMBlock {
uint8_t *host; //对应宿主的内存地址hva
ram_addr_t offset; //block在ramlist中的偏移 gpa
ram_addr_t length; //block长度
char idstr[256]; //block名字
QLIST_ENTRY(RAMBlock) next;
} RAMBlock;
typedef struct RAMList {
uint8_t *phys_dirty; // list的head
QLIST_HEAD(ram, RAMBlock) blocks;
} RAMList;

2、MemoryRegion
管理虚拟机内存,通过内存属性,GUEST物理地址等特点对内存分类,就形成了多个MemoryRegion,这些MemoryRegion 通过树状组织起来,挂接到根MemoryRegion下。每个MemoryRegion树代表了一类作用的内存,qemu中两个全局的MemoryRegion,分别是system_memory和system_io
为了方便描述,将MemoryRegion分为三类:
1. 根MemoryRegion:不分配真正的物理内存,通过subregions将所有的子MemoryRegion管理起来,如图中的system_memory
2. 实体MemoryRegion:这种MemoryRegion中真正的分配物理内存,最主要的就是pc.ram和pci。分配的物理内存的作用分别是内存、PCI地址空间以及fireware空间。QEMU是用户空间代码,分配的物理内存返回的是HVA,被保存到host域。同时这个结构还会为本段虚拟机内存分配虚拟机物理地址空间起始地址,该起始地址(GPA)保存到ram_addr域,该段内存大小为size。通过实体MemoryRegion就可以将HOST地址HVA和GUEST地址GPA对应起来,这种实体MemoryRegion起到了转换的作用。
3. 别名MemoryRegion:这种MemoryRegion中不分配物理内存,代表了实体MemoryRegion的一个部分,通过alias域指向实体MemoryRegion,alias_offset代表了该别名MemoryRegion所代表内存起始GPA相对于实体MemoryRegion所代表内存起始GPA的偏移量,通常用来计算别名MemoryRegion对应的物理内存的HVA值:HVA = 起始HVA + alias_offset。如图中的ram_above_4g和ram-below-4g
3、Address_space
(qemu的内存管理在交付给KVM管理时,中间又加了一个抽象层。MR管理的host的内存,那么address_space管理的更偏向于虚拟机。把MR映射到虚拟机的物理地址空间)
不管是DRAM还是设备的资源都要通过memory region添加到address space里。
本来不同的设备使用的地址空间不同,但是QEMU X86里面只有两种,address_space_memory(虚机的线性地址空间(设备的mmio分布在这个地址空间))和address_space_io(虚机的io地址空间(设备的io port就分布在这个地址空间里))。
初始化:main -> cpu_exec_init_all -> memory_map_init -> address_space_init(system_memory) 和 address_space_init(system_io)-> TAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link); 最后调用memory_region_transaction_commit()提交本次修改。
注册为回调(保持内核和用户空间的内存信息的一致性):kvm_init最后执行memory_listener_register,注册了address_space_memory和address_space_io两个。涉及的另外一个结构体则是MemoryListener,有kvm_memory_listener和kvm_io_listener,就是用于监控内存映射关系发生变化之后执行回调函数
实际内存分配:下面的pc_memory_init:分配全局ram(一整个memory region),然后根据below_4g_mem_size、above_4g_mem_size分别对ram进行划分,形成子MR,并注册子MR到root MR system_memory 的subregions链表中。最后需要调用memory_region_transaction_commit()函数提交修改。
4、Flatview、Flatrange、MemoryRegionSection
(每个FlatRange对应一段虚拟机物理地址区间,各个FlatRange不会重叠,按照地址的顺序保存在数组中)
MR和Flatview关系:MemoryRegion是QEMU管理内存的树状结构,便于按照功能、属性分类;但这只是管理结构。但虚拟机的内存需要通过KVM_SET_USER_MEMORY_REGION,将HVA和GPA的对应关系注册到KVM模块的memslot,才可以生效成为EPT。如果QEMU直接使用MemoryRegion进行注册,那么注册的过程将会很麻烦,也容易不断的出现重叠判断等。所以在通过KVM_SET_USER_MEMORY_REGION注册前,加了一层转换机制,先将树状的MemoryRegion展开物理内存样子的一维区间结构,然后再通过KVM_SET_USER_MEMORY_REGION将这个展开的物理内存注册到KVM内核模块中,就方便了许多。这个转换机制是FlatView模型。整个转换过程:函数address_space_update_topology,将指定的AddressSpace下的MemoryRegion树进行展平,形成了对应一维内存逻辑表示的FlatView,然后再address_space_update_topology_pass中将FlatView模型通过KVM_SET_USER_MEMORY_REGION注册到KVM模块中。
当memory region发生变化的时候,执行memory_region_transaction_commit,address_space_update_topology,address_space_update_topology_pass最终完成更新FlatView的目标。【此处就涉及到KVM中更新kvm->memslots】
Flatview原理:
1. 首先FlatView模型是通过FlatView和FlatRange两个对象组成。
2. FlatView是该段内存的整体视图的管理结构,一个FlatView由一组FlatRange组成。
3. 每个FlatRange代表了虚拟机上的一段内存,多个FlagRange就组成了一个内存视图,这些FlatRange在物理地址空间上不一定是相邻的。
3. 每个FlatView代表了某一类内存的组合,用作特殊的用途(如系统内存空间,MMIO内存地址空间),通常一个FlatView同一个特定用途的Address_Space进行关联
4. 每个FlatRange通过AddrRange标记该段GUEST内存的大小和长度。
5. FlatRange数组在FlatView初始化的时候为0个,也就是没有分配数组。当进行flatview_insert()的操作的时候,才会动态分配出来
6. 为了简化FlatView,通常将地址空间上连续的FlatRange进行合并,合并为1个FlatRange。如图中的r3,r4,r5就可以进行合并的区间,合并后都合并为r3。r4和r5的内容将被后面的FlatRange的数组元素覆盖掉

每一个flat range都投射到同一个地址空间的平面上,而上图中的R1,R2等对应的则是struct MemoryRegionSection(MemoryRegionSection对应于FlatRange,一个FlatRange代表一个物理地址空间的片段,但是其偏向于address-space,而MemoryRegionSection则在MR端显示的表明了分片)
5、KVMslot、kvm_userspace_memory_region
(更接近kvm)
在MEMORY_LISTENER_CALL中调用kvm_region_add和kvm_region_del,执行kvm_set_phys_mem,组装KVMSlot,再把对应信息转给kvm_userspace_memory_region,将其通过kvm_vm_ioctl传给KVM用于更新kvm->memslots
2、qemu到kvm实际分配vm内存流程
QEMU在pc_init1调用pc_cpus_init创建完vcpu返回后,走到初始化内存pc_memory_init-》
1、memory_region_init_ram-》qemu_ram_alloc-》qemu_ram_alloc_from_ptr-》
(1)使用find_ram_offset赋值给new block的offset(find_ram_offset在线性区间内找到没有使用的一段空间,可以完全容纳新申请的ramblock length大小,找到满足新申请length的最小区间,把ramblock安插进去即可,返回的offset即是新分配区间的开始地址);
(2)new_block->host = kvm_vmalloc(size) 分配真正物理内存,内部qemu_vmalloc使用qemu_memalign页对齐分配内存。后续的都是对RAMBlock的插入等处理。
以上:memory_region_init_ram已经将qemu内存模型和实际的物理内存初始化了(严格意义讲真实内存空间分配,是在QEMU发生缺页host做分配的时候。tdp_page_fault函数只是做的guest物理地址到host线性地址的映射关系,不真正涉及guest真实物理空间的分配。)
2、从memory_region_init_ram退出到pc_memory_init时已经初始化完成MemoryRegion ram,然后执行vmstate_register_ram_global,负责将前面提到的ramlist中的ramblock和memory region的初始地址对应一下,将mr->name填充到ramblock的idstr里面,就是让二者有确定的对应关系,如此mr就有了物理内存使用
3、memory_region_add_subregion-》memory_region_transaction_commit修改虚拟机的内存。引入了新的结构address_spaces(AS),对所有AS执行address_space_update_topology该函数内address_space_get_flatview直接获取当前的FlatView,然后generate_memory_topology根据前面已经变化的mr重新生成FlatView,然后调用address_space_update_topology_pass:
(1)两个FlatView逐条的FlatRange进行对比,以后一个FlatView为准,如果前面FlatView的FlatRange和后面的不一样,则对前面的FlatView的这条FlatRange进行处理。
(2)比较结束后,主要是MEMORY_LISTENER_UPDATE_REGION函数,将变化的FlatRange构造一个MemoryRegionSection,然后遍历所有的memory_listeners,如果memory_listeners监控的内存区域和MemoryRegionSection一样,则执行第四个入参函数,如region_del函数,即kvm_region_del函数,这个是在kvm_init中初始化的(不一样则利用kvm_region_add添加内存到KVM的kvm->memslots:)。
kvm_region_del主要是kvm_set_phys_mem函数,主要是将MemoryRegionSection有效值转换成KVMSlot形式,然后调用kvm_set_user_memory_region中使用kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);传递给kernel。(其中mem就是kvm_userspace_memory_region结构)
参考:
qemu中的内存管理的更多相关文章
- Unity游戏开发中的内存管理_资料
内存是手游的硬伤——Unity游戏Mono内存管理及泄漏http://wetest.qq.com/lab/view/135.html 深入浅出再谈Unity内存泄漏http://wetest.qq.c ...
- C++中的内存管理
在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete. new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存 ...
- cocos2dx中的内存管理机制及引用计数
1.内存管理的两大策略: 谁申请,谁释放原则(类似于,谁污染了内存,最后由谁来清理内存)--------->适用于过程性函数 引用计数原则(创建时,引用数为1,每引用一次,计数加1,调用结束时, ...
- Cocos2d-x开发中C++内存管理
由于开始并没有介绍C++语言,C++的内存管理当然也没进行任何的说明,为了掌握Cocos2d-x中的内存管理机制,是有必要先了解一些C++内存管理的知识.C++内存管理非常复杂,如果完全地系统地介绍可 ...
- 6.关于QT中的内存管理,动态的制作,动态库的调用,静态库的制作
一 QT的内存管理 1 QT中的内存管理是QObject来管理的 2 QT中的内存管理没有cocos2dx中的引用计数 3 组件能够指定父对象 QTimer *timer = QTime ...
- C语言中的内存管理
开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容.不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰. 先说一下C语言中的内存管 ...
- JNI中的内存管理(转)
源:JNI中的内存管理 JNI 编程简介 JNI,Java Native Interface,是 native code 的编程接口.JNI 使 Java 代码程序可以与 native code 交互 ...
- javascript中的内存管理和垃圾回收
前面的话 不管什么程序语言,内存生命周期基本是一致的:首先,分配需要的内存:然后,使用分配到的内存:最后,释放其内存.而对于第三个步骤,何时释放内存及释放哪些变量的内存,则需要使用垃圾回收机制.本文将 ...
- php中的内存管理的介绍(转)
本篇文章给大家带来的内容是关于php中的内存管理的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 一.php内存管理概述——Zend引擎 由于计算机的内存由操作系统进行管理,所以 ...
随机推荐
- react-native获取设备信息组件(react-native-device-info)
转载链接:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/react-native-acquisition-device-infor ...
- 马昕璐 201771010118《面向对象程序设计(java)》第六周学习总结
第一部分:理论知识学习部分 1.继承 继承:用已有类来构建新类的一种机制.当定义了一个新类继承了一个类时,这个新类就继承了这个类的方法和域,同时在新类中添加新的方法和域以适应新的情况. 继承是Java ...
- 函数防抖 & 函数节流
避免一个函数频繁执行 - 避免程序卡顿 js 是单线程的,setTimeout 这样的函数是异步的 异步的代码,交给对应的模块进行处理 模块在会将异步任务,在主线程执行完所有同步代码后,加入事件队列 ...
- Codeforces Round #547 (Div. 3)
我老人家走了四公里吃个汉堡还没吃成.垃圾肯德基.垃圾春分半价桶. 蜜雪冰城百香果加冰+烤串真是爽死了.原来二十多块钱可以吃的这么爽. A: #include <bits/stdc++.h> ...
- Pandora 生成 Token
生成 token 打数据到仓库 通过 api 签名工具实现 最后通过curl -XPOST -H "Content-Type: application/json" -H " ...
- 基于贝叶斯算法实现简单的分类(java)
参考文章:https://blog.csdn.net/qq_32690999/article/details/78737393 项目代码目录结构 模拟训练的数据集 核心代码 Bayes.java pa ...
- 人工智能--AI篇
AI背景 在当今互联网信息高速发展的大背景下,人工智能(AI)已经开始走进了千家万户,逐渐和我们的生活接轨,那具体什么是AI呢? 什么是人工智能(AI)? 人工智能:简单理解就是由人制造出来的,有一定 ...
- 动态规划 hdu 1024
Max Sum Plus Plus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- 《ABCD组团队》第二次作业
ABCD组:二手车价格预测系统 项目 内容 这个作业属于哪个课程 http://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.c ...
- 周末没事干就看CSS JS Python ThinkPHP的书,照着例子运行就行,可以增强信心(www.delphihtmlcomponents.com 是神器,也可以帮助我学习。还有虚拟机运行Web)
https://www.javatpoint.com/javascript-tutorialhttps://www.javatpoint.com/html-tutorialhttps://www.ja ...