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

88 篇文章 19 订阅
本文深入探讨QEMU中的MemoryRegion结构,它是内存模拟的核心,构成无环图来模拟整个内存。MemoryRegion是树状结构,表示虚拟机内存区域,包括RAM、MMIO等类型。内存区域通过MemoryRegionOps的回调函数处理读写操作,具有层级关系和优先级,用于管理虚拟机的物理地址空间和设备模拟。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析 —— 内存虚拟化(2)

本文内容参考:

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

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

QEMU内存管理模型

浅谈QEMU Memory Region 与 Address Space

特此致谢!

QEMU 内存初始化

1. 基本结构

上一回讲解了QEMU中与内存相关的第一个数据结构AddressSpace,并初步介绍了第二个基本数据结MemoryRegion以及这两者之间的关系。本回对于MemoryRegion结构进行深入讲解。

(2)MemoryRegion

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

MemoryRegion是一个树状结构。每个MemoryRegion里会有一个链表头维护child region,还有一个链表单元subregions_link用于挂接到上一级的sub_region链表上,还有向上查找的container指针指向父节点。所以MemoryRegion并不是并列的多个地址块组成(一级链表维护就可以),而是有层级关系的。

为了便于理解,再次贴出struct MemoryRegion的定义,在include/exec/memory.h中,代码如下:

  1. /** MemoryRegion:
  2. *
  3. * A struct representing a memory region.
  4. */
  5. struct MemoryRegion {
  6. Object parent_obj;
  7. /* private: */
  8. /* The following fields should fit in a cache line */
  9. bool romd_mode;
  10. bool ram;
  11. bool subpage;
  12. bool readonly; /* For RAM regions */
  13. bool nonvolatile;
  14. bool rom_device;
  15. bool flush_coalesced_mmio;
  16. uint8_t dirty_log_mask;
  17. bool is_iommu;
  18. RAMBlock *ram_block;
  19. Object *owner;
  20. /* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */
  21. DeviceState *dev;
  22. const MemoryRegionOps *ops;
  23. void *opaque;
  24. MemoryRegion *container;
  25. int mapped_via_alias; /* Mapped via an alias, container might be NULL */
  26. Int128 size;
  27. hwaddr addr;
  28. void (*destructor)(MemoryRegion *mr);
  29. uint64_t align;
  30. bool terminates;
  31. bool ram_device;
  32. bool enabled;
  33. bool warning_printed; /* For reservations */
  34. uint8_t vga_logging_count;
  35. MemoryRegion *alias;
  36. hwaddr alias_offset;
  37. int32_t priority;
  38. QTAILQ_HEAD(, MemoryRegion) subregions;
  39. QTAILQ_ENTRY(MemoryRegion) subregions_link;
  40. QTAILQ_HEAD(, CoalescedMemoryRange) coalesced;
  41. const char *name;
  42. unsigned ioeventfd_nb;
  43. MemoryRegionIoeventfd *ioeventfds;
  44. RamDiscardManager *rdm; /* Only for RAM */
  45. /* For devices designed to perform re-entrant IO into their own IO MRs */
  46. bool disable_reentrancy_guard;
  47. };

对其中比较重要的成员说明如下:

  • RAMBlock *ram_block

ram_block表示实际分配的物理内存。后文书会详细分析。

  • const MemoryRegionOps *ops

ops 里面是一组回调函数,在对MemoryRegion进行操作时会被调用,如MMIO的读写请求。

  • MemoryRegion *container

container表示该MemoryRegion所处的上一级MemoryRegion。

  • hwaddr addr

addr表示该MemoryRegion所在的虚拟机的物理地址。

  • bool terminates

terminates用来指示是否是叶子节点。

  • int32_t priority

priority用于指示MemoryRegion的优先级。

  • QTAILQ_HEAD(, MemoryRegion) subregions

subregions将该MemoryRegion所属的子MemoryRegion连接起来。

  • QTAILQ_ENTRY(MemoryRegion) subregions_link

subregions_link则用来连接同一个父MemoryRegion下的相同兄弟。

为了便于理解,再次贴出上一回的AddressSpace与MemoryRegion关系图,其实也是MemoryRegion结构拓扑图。

643ccd90878123b62334822412c9158c.png

关于MemoryRegion,有以下更多、更进一步说明:

1)MemoryRegion里有基础的hwaddr和size,是针对GUEST视角的地址,即GPA(Guest Physical Addr,客户机物理地址)。还有一个offset;

2)父子MemoryRegion之间是从属关系,每个MemoryRegion涵盖自己所有的child region空间;

3)同一个MemoryRegion下的child列表是并列的,但未必是互斥的,如果有重复的地址空间,则会根据优先级来选择;

4)最上层的MemoryRegion记录在AddressSpace的root变量;

5)有一点需要注意,QEMU里面维护的是整个地址空间,因此MemoryRegion虽然名字是“Memory”,但实际上它并非只针对内存。除了内存,还包括PCI bar空间(memory)、config空间(io/memory)等。guest对所有地址空间的访问,都要由它接管。

常见的MemoryRegion有如下几类:

  • RAM

host上 一段实际 分配给虚拟机作为物理内存的虚拟内存 。内存Memory和Cache就是RAM类型。

创建接口为:

memory_region_init_ram() —— 通用的

memory_region_init_resizeable_ram()、 memory_region_init_ram_from_file()、memory_region_init_ram_ptr() —— 特殊的

  • MMIO

guest的一段内存 ,但是 在宿主机上没有对应的虚拟内存 ,而是截获对这个区域的访问,调用对应读写函数,用在设备模拟中。

用于host callbacks创建的guest memory,如每次read or write操作会导致host的一个callback。

创建接口为:

memory_region_init_io()

  • ROM

与RAM类似,只是该类型内存只有只读属性,无法写入。主要用于闪存,比如存OS的加载器。

创建接口为:

memory_region_init_rom()

  • ROM device

其在读方面类似于RAM,而在写方面类似于MMIO,写入会调用对应的写回调函数。像RAM一样read,同时write会调用host callback的device。

创建接口为:

memory_region_init_rom_device()

  • IOMMU

有IOMMU职责的region,即能将地址直接翻译到目标的memory region,只能用于IOMMU device。

创建接口为:

memory_region_init_iommu()

  • container

包含若干个MemoryRegion,每一个Region在这个container中的偏移都不一样 。container主要用来将多个MemoryRegion合并成一个,如PCI的MemoryRegion就会包括RAM的MMIO。一般来说,container中的region不会重合,但有的时候也有例外。

顾名思义,包含了其它的mr,记录每个mr的offset。container可以管理多个mr为一个单位,十分有用。例如,一个PCI BAR可能包含了一个RAM region和一个MMIO region。

创建接口为:

memory_region_init()

  • alias

region的另一部分,可以使一个region被分成几个不连续的部分。

通常情况下,MemoryRegion并不会重叠,当解析一个地址时,只会落入一个MemoryRegion;而有些时候,让MemoryRegion重合也比较有用。但是当MemoryRegion重合时,就需要有一种机制决定到底让哪一个对虚拟机可见,这就是MemoryRegion结构体中priority的作用。

一个region的子集。允许将一个region分解成不连续的子region。

创建接口为:

memory_region_add_subregion() —— 用于将一个已存在的region加入到某个container中

memory_region_init_alias() —— 用于创建一个新的region

以上是从具体的MemoryRegion类型来划分的,从更大的层面可以将MemoryRegion划分为以下三种:

  • 根级MemoryRegion

直接 通过memory_region_init函数初始化 没有自己的内存 ,用于管理subregion。如 system_memory。

  • 实体MemoryRegion

通过memory_region_init_ram函数初始化 有自己的内存 (从QEMU进程地址空间中分配),大小为 size。如ram_memory(pc.ram)、pci_memory(pci)等。 这种MemoryRegion中真正的分配物理内存,最主要的就是pc.ram和pci。分配的物理内存的作用分别是内存、PCI地址空间以及fireware空间。QEMU是用户空间代码,分配的物理内存返回的是HVA(Host Virtual Address ),HVA保存至RAMBlock的host域。通过实体MemoryRegion对应的RAMBlock可以管理HVA。

  • 别名MemoryRegion

通过memory_region_init_alias函数初始化 没有自己的内存 表示实体MemoryRegion (如 pc.ram) 的一部分 。通过alias成员指向实体MemoryRegion,alias_offset代表了该别名MemoryRegion所代表的内存起始GPA相对于实体MemoryRegion所代表的内存起始GPA的偏移量,如ram_below_4g、ram_above_4g等。

39eea3608c2790fa6ea1027a7aaeefbe.png

更多内容请看下回。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他