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

88 篇文章 19 订阅
本文深入讲解QEMU内存管理中的RAMBlock结构,它表示虚拟机的内存条,记录内存条的基本信息,如物理内存大小、文件后端、内存偏移等。RAMBlock与MemoryRegion的关系以及在内存模拟中的作用被详细阐述。
摘要由CSDN通过智能技术生成

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

本文内容参考:

《趣谈 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中,代码如下:

  1. struct RAMBlock {
  2. struct rcu_head rcu;
  3. struct MemoryRegion *mr;
  4. uint8_t *host;
  5. uint8_t *colo_cache; /* For colo, VM's ram cache */
  6. ram_addr_t offset;
  7. ram_addr_t used_length;
  8. ram_addr_t max_length;
  9. void (*resized)(const char*, uint64_t length, void *host);
  10. uint32_t flags;
  11. /* Protected by iothread lock. */
  12. char idstr[256];
  13. /* RCU-enabled, writes protected by the ramlist lock */
  14. QLIST_ENTRY(RAMBlock) next;
  15. QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;
  16. int fd;
  17. uint64_t fd_offset;
  18. size_t page_size;
  19. /* dirty bitmap used during migration */
  20. unsigned long *bmap;
  21. /* bitmap of already received pages in postcopy */
  22. unsigned long *receivedmap;
  23. /*
  24. * bitmap to track already cleared dirty bitmap. When the bit is
  25. * set, it means the corresponding memory chunk needs a log-clear.
  26. * Set this up to non-NULL to enable the capability to postpone
  27. * and split clearing of dirty bitmap on the remote node (e.g.,
  28. * KVM). The bitmap will be set only when doing global sync.
  29. *
  30. * It is only used during src side of ram migration, and it is
  31. * protected by the global ram_state.bitmap_mutex.
  32. *
  33. * NOTE: this bitmap is different comparing to the other bitmaps
  34. * in that one bit can represent multiple guest pages (which is
  35. * decided by the `clear_bmap_shift' variable below). On
  36. * destination side, this should always be NULL, and the variable
  37. * `clear_bmap_shift' is meaningless.
  38. */
  39. unsigned long *clear_bmap;
  40. uint8_t clear_bmap_shift;
  41. /*
  42. * RAM block length that corresponds to the used_length on the migration
  43. * source (after RAM block sizes were synchronized). Especially, after
  44. * starting to run the guest, used_length and postcopy_length can differ.
  45. * Used to register/unregister uffd handlers and as the size of the received
  46. * bitmap. Receiving any page beyond this length will bail out, as it
  47. * could not have been valid on the source.
  48. */
  49. ram_addr_t postcopy_length;
  50. };

上边已提到,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中,如下:

  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. ……
  20. };
  • 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中,代码如下:

  1. /* address in the RAM (different from a physical address) */
  2. #if defined(CONFIG_XEN_BACKEND)
  3. typedef uint64_t ram_addr_t;
  4. # define RAM_ADDR_MAX UINT64_MAX
  5. # define RAM_ADDR_FMT "%" PRIx64
  6. #else
  7. typedef uintptr_t ram_addr_t;
  8. # define RAM_ADDR_MAX UINTPTR_MAX
  9. # define RAM_ADDR_FMT "%" PRIxPTR
  10. #endif

uint64_t和uintptr_t都在roms/opensbi/include/sbi/sbi_types.h中定义,分别如下:

  1. #if __riscv_xlen == 64
  2. typedef long s64;
  3. typedef unsigned long u64;
  4. typedef long int64_t;
  5. typedef unsigned long uint64_t;
  6. #define PRILX "016lx"
  7. #elif __riscv_xlen == 32
  8. typedef long long s64;
  9. typedef unsigned long long u64;
  10. typedef long long int64_t;
  11. typedef unsigned long long uint64_t;
  12. #define PRILX "08lx"
  13. #else
  14. #error "Unexpected __riscv_xlen"
  15. #endif
typedef unsigned long		uintptr_t;

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

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

  • AddressSpace(struct AddressSpace)

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

  1. /**
  2. * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects
  3. */
  4. struct AddressSpace {
  5. /* private: */
  6. struct rcu_head rcu;
  7. char *name;
  8. MemoryRegion *root;
  9. /* Accessed via RCU. */
  10. struct FlatView *current_map;
  11. int ioeventfd_nb;
  12. struct MemoryRegionIoeventfd *ioeventfds;
  13. QTAILQ_HEAD(, MemoryListener) listeners;
  14. QTAILQ_ENTRY(AddressSpace) address_spaces_link;
  15. };
  • MemoryRegion(struct MemoryRegion)

MemoryRegion表示的是虚拟机中的一段内存区域。MemoryRegion是内存模拟中的核心结构,整个内存的模拟都是通过MemoryRegion构成的无环图完成的。图的叶子节点是实际分配给虚拟机的物理内存或者MMIO,中间节点则表示内存总线,内存控制是其它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(struct RAMBlock)

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

  1. struct RAMBlock {
  2. struct rcu_head rcu;
  3. struct MemoryRegion *mr;
  4. uint8_t *host;
  5. uint8_t *colo_cache; /* For colo, VM's ram cache */
  6. ram_addr_t offset;
  7. ram_addr_t used_length;
  8. ram_addr_t max_length;
  9. void (*resized)(const char*, uint64_t length, void *host);
  10. uint32_t flags;
  11. /* Protected by iothread lock. */
  12. char idstr[256];
  13. /* RCU-enabled, writes protected by the ramlist lock */
  14. QLIST_ENTRY(RAMBlock) next;
  15. QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers;
  16. int fd;
  17. uint64_t fd_offset;
  18. size_t page_size;
  19. /* dirty bitmap used during migration */
  20. unsigned long *bmap;
  21. /* bitmap of already received pages in postcopy */
  22. unsigned long *receivedmap;
  23. /*
  24. * bitmap to track already cleared dirty bitmap. When the bit is
  25. * set, it means the corresponding memory chunk needs a log-clear.
  26. * Set this up to non-NULL to enable the capability to postpone
  27. * and split clearing of dirty bitmap on the remote node (e.g.,
  28. * KVM). The bitmap will be set only when doing global sync.
  29. *
  30. * It is only used during src side of ram migration, and it is
  31. * protected by the global ram_state.bitmap_mutex.
  32. *
  33. * NOTE: this bitmap is different comparing to the other bitmaps
  34. * in that one bit can represent multiple guest pages (which is
  35. * decided by the `clear_bmap_shift' variable below). On
  36. * destination side, this should always be NULL, and the variable
  37. * `clear_bmap_shift' is meaningless.
  38. */
  39. unsigned long *clear_bmap;
  40. uint8_t clear_bmap_shift;
  41. /*
  42. * RAM block length that corresponds to the used_length on the migration
  43. * source (after RAM block sizes were synchronized). Especially, after
  44. * starting to run the guest, used_length and postcopy_length can differ.
  45. * Used to register/unregister uffd handlers and as the size of the received
  46. * bitmap. Receiving any page beyond this length will bail out, as it
  47. * could not have been valid on the source.
  48. */
  49. ram_addr_t postcopy_length;
  50. };

39eea3608c2790fa6ea1027a7aaeefbe.png

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

举报

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