QEMU源码全解析23 —— QOM介绍(12)

88 篇文章 19 订阅
本文详细解析QEMU中QOM(Quantum Object Model)的实现,探讨对象构造、初始化及其与类型信息之间的关系。通过 EduState、PCIDevice 和 DeviceState 的例子,阐述QOM中对象的父子关系,并类比Java反射机制进行说明。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析22 —— QOM介绍(11)

本文内容参考:

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

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

特此致谢!

上一回分析了 QEMU 对象的构造和初始化的函数调用流程,本回结合实例对此流程进行深入介绍和解析。

仍以前文的edu的TypeInfo为例。再次贴出edu相关代码如下(hw/misc/edu.c中):

  1. static void pci_edu_register_types(void)
  2. {
  3. static InterfaceInfo interfaces[] = {
  4. { INTERFACE_CONVENTIONAL_PCI_DEVICE },
  5. { },
  6. };
  7. static const TypeInfo edu_info = {
  8. .name = TYPE_PCI_EDU_DEVICE,
  9. .parent = TYPE_PCI_DEVICE,
  10. .instance_size = sizeof(EduState),
  11. .instance_init = edu_instance_init,
  12. .class_init = edu_class_init,
  13. .interfaces = interfaces,
  14. };
  15. type_register_static(&edu_info);
  16. }
  17. type_init(pci_edu_register_types)

edu的对象大小为sizeof(EduState),因此实际上一个edu类型的对象是EduState结构体,每一个对象都会有一个XXXState与之对应,记录了该对象的相关信息。若edu是一个PCI设备,则EduState中就会有这个设备的一些信息,如:中断信息、设备状态、使用的MMIO和PIO对应的内存区域等。

在object_init_with_type函数中可以看到,调用的参数都是一个Object,却能够一直调用父类型的初始化函数,不出意外这里也会有一个层次关系。为了便于理解,再次贴出相关代码:

  1. static void object_init_with_type(Object *obj, TypeImpl *ti)
  2. {
  3. if (type_has_parent(ti)) {
  4. object_init_with_type(obj, type_get_parent(ti));
  5. }
  6. if (ti->instance_init) {
  7. ti->instance_init(obj);
  8. }
  9. }

具体来看一下EduState结构,其定义在hw/misc/edu.c中,如下所示:

  1. struct EduState {
  2. PCIDevice pdev;
  3. MemoryRegion mmio;
  4. QemuThread thread;
  5. QemuMutex thr_mutex;
  6. QemuCond thr_cond;
  7. bool stopping;
  8. uint32_t addr4;
  9. uint32_t fact;
  10. #define EDU_STATUS_COMPUTING 0x01
  11. #define EDU_STATUS_IRQFACT 0x80
  12. uint32_t status;
  13. uint32_t irq_status;
  14. #define EDU_DMA_RUN 0x1
  15. #define EDU_DMA_DIR(cmd) (((cmd) & 0x2) >> 1)
  16. # define EDU_DMA_FROM_PCI 0
  17. # define EDU_DMA_TO_PCI 1
  18. #define EDU_DMA_IRQ 0x4
  19. struct dma_state {
  20. dma_addr_t src;
  21. dma_addr_t dst;
  22. dma_addr_t cnt;
  23. dma_addr_t cmd;
  24. } dma;
  25. QEMUTimer dma_timer;
  26. char dma_buf[DMA_SIZE];
  27. uint64_t dma_mask;
  28. };

关注其中第一个成员的类型:PCIDevice结构。其与pci_device_type_info对应,代码如下(hw/pci/pci.c中):

  1. static const TypeInfo pci_device_type_info = {
  2. .name = TYPE_PCI_DEVICE,
  3. .parent = TYPE_DEVICE,
  4. .instance_size = sizeof(PCIDevice),
  5. .abstract = true,
  6. .class_size = sizeof(PCIDeviceClass),
  7. .class_init = pci_device_class_init,
  8. .class_base_init = pci_device_class_base_init,
  9. };

struct PCIDevice在include/hw/pci/pci.h中定义,代码如下:

  1. struct PCIDevice {
  2. DeviceState qdev;
  3. bool partially_hotplugged;
  4. bool has_power;
  5. /* PCI config space */
  6. uint8_t *config;
  7. /* Used to enable config checks on load. Note that writable bits are
  8. * never checked even if set in cmask. */
  9. uint8_t *cmask;
  10. /* Used to implement R/W bytes */
  11. uint8_t *wmask;
  12. /* Used to implement RW1C(Write 1 to Clear) bytes */
  13. uint8_t *w1cmask;
  14. /* Used to allocate config space for capabilities. */
  15. uint8_t *used;
  16. /* the following fields are read only */
  17. int32_t devfn;
  18. /* Cached device to fetch requester ID from, to avoid the PCI
  19. * tree walking every time we invoke PCI request (e.g.,
  20. * MSI). For conventional PCI root complex, this field is
  21. * meaningless. */
  22. PCIReqIDCache requester_id_cache;
  23. char name[64];
  24. PCIIORegion io_regions[PCI_NUM_REGIONS];
  25. AddressSpace bus_master_as;
  26. MemoryRegion bus_master_container_region;
  27. MemoryRegion bus_master_enable_region;
  28. /* do not access the following fields */
  29. PCIConfigReadFunc *config_read;
  30. PCIConfigWriteFunc *config_write;
  31. /* Legacy PCI VGA regions */
  32. MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS];
  33. bool has_vga;
  34. /* Current IRQ levels. Used internally by the generic PCI code. */
  35. uint8_t irq_state;
  36. /* Capability bits */
  37. uint32_t cap_present;
  38. /* Offset of MSI-X capability in config space */
  39. uint8_t msix_cap;
  40. /* MSI-X entries */
  41. int msix_entries_nr;
  42. /* Space to store MSIX table & pending bit array */
  43. uint8_t *msix_table;
  44. uint8_t *msix_pba;
  45. /* May be used by INTx or MSI during interrupt notification */
  46. void *irq_opaque;
  47. MSITriggerFunc *msi_trigger;
  48. MSIPrepareMessageFunc *msi_prepare_message;
  49. MSIxPrepareMessageFunc *msix_prepare_message;
  50. /* MemoryRegion container for msix exclusive BAR setup */
  51. MemoryRegion msix_exclusive_bar;
  52. /* Memory Regions for MSIX table and pending bit entries. */
  53. MemoryRegion msix_table_mmio;
  54. MemoryRegion msix_pba_mmio;
  55. /* Reference-count for entries actually in use by driver. */
  56. unsigned *msix_entry_used;
  57. /* MSIX function mask set or MSIX disabled */
  58. bool msix_function_masked;
  59. /* Version id needed for VMState */
  60. int32_t version_id;
  61. /* Offset of MSI capability in config space */
  62. uint8_t msi_cap;
  63. /* PCI Express */
  64. PCIExpressDevice exp;
  65. /* SHPC */
  66. SHPCDevice *shpc;
  67. /* Location of option rom */
  68. char *romfile;
  69. uint32_t romsize;
  70. bool has_rom;
  71. MemoryRegion rom;
  72. uint32_t rom_bar;
  73. /* INTx routing notifier */
  74. PCIINTxRoutingNotifier intx_routing_notifier;
  75. /* MSI-X notifiers */
  76. MSIVectorUseNotifier msix_vector_use_notifier;
  77. MSIVectorReleaseNotifier msix_vector_release_notifier;
  78. MSIVectorPollNotifier msix_vector_poll_notifier;
  79. /* ID of standby device in net_failover pair */
  80. char *failover_pair_id;
  81. uint32_t acpi_index;
  82. };

同样关注第一个成员的类型:DeviceState结构。其与device_type_info对应,代码如下(hw/core/qdev.c):

  1. static const TypeInfo device_type_info = {
  2. .name = TYPE_DEVICE,
  3. .parent = TYPE_OBJECT,
  4. .instance_size = sizeof(DeviceState),
  5. .instance_init = device_initfn,
  6. .instance_post_init = device_post_init,
  7. .instance_finalize = device_finalize,
  8. .class_base_init = device_class_base_init,
  9. .class_init = device_class_init,
  10. .abstract = true,
  11. .class_size = sizeof(DeviceClass),
  12. .interfaces = (InterfaceInfo[]) {
  13. { TYPE_VMSTATE_IF },
  14. { TYPE_RESETTABLE_INTERFACE },
  15. { }
  16. }
  17. };

struct DeviceState在include/hw/qdev-core.h中定义,代码如下:

  1. /**
  2. * DeviceState:
  3. * @realized: Indicates whether the device has been fully constructed.
  4. * When accessed outside big qemu lock, must be accessed with
  5. * qatomic_load_acquire()
  6. * @reset: ResettableState for the device; handled by Resettable interface.
  7. *
  8. * This structure should not be accessed directly. We declare it here
  9. * so that it can be embedded in individual device state structures.
  10. */
  11. struct DeviceState {
  12. /*< private >*/
  13. Object parent_obj;
  14. /*< public >*/
  15. char *id;
  16. char *canonical_path;
  17. bool realized;
  18. bool pending_deleted_event;
  19. int64_t pending_deleted_expires_ms;
  20. QDict *opts;
  21. int hotplugged;
  22. bool allow_unplug_during_migration;
  23. BusState *parent_bus;
  24. QLIST_HEAD(, NamedGPIOList) gpios;
  25. QLIST_HEAD(, NamedClockList) clocks;
  26. QLIST_HEAD(, BusState) child_bus;
  27. int num_child_bus;
  28. int instance_id_alias;
  29. int alias_required_for_version;
  30. ResettableState reset;
  31. GSList *unplug_blockers;
  32. };

通过以上edu_info、pci_device_type_info、device_type_info和与之对应的EduState、PCIDevice、DeviceState的定义可以看出,对象之间其实也是有一种父对象与子对象的关系存在。与类型一样,QOM中的对象也可以使用宏,将一个指向Object对象的指针转换成一个指向子类对象的指针,转换过程与类型ObjectClass类似,在此不在赘述。

这里可以看出,不同于类型信息和类型,object是根据需要创建的,只有在命令行指定了设备或者是热插拔一个设备之后才会有object的创建。类型和对象之间是通过Object的Class域联系在一起的。这是在object_initialize_with_type函数中通过obj->class = type->class实现的。对应代码再次贴出(qom/object.c中):

  1. static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type)
  2. {
  3. type_initialize(type);
  4. g_assert(type->instance_size >= sizeof(Object));
  5. g_assert(type->abstract == false);
  6. g_assert(size >= type->instance_size);
  7. memset(obj, 0, type->instance_size);
  8. obj->class = type->class;
  9. object_ref(obj);
  10. object_class_property_init_all(obj);
  11. obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
  12. NULL, object_property_free);
  13. object_init_with_type(obj, type);
  14. object_post_init_with_type(obj, type);
  15. }

综合前文(从QEMU源码全解析 —— QOM介绍(1)开始),可以把QOM的对象构造成三部分:第一部分是类型的构造,通过TypeInfo构造一个TypeImpl的哈希表,这是在主函数main之前完成的;第二部分是类型的初始化,这两部分都是全局的,即只要编译进去的QOM对象都会被调用;第三部分是类对象的构造,这是构造具体的对象实例,只有在命令行指定了对应的设备时,才会创建对象。

类比Java中的反射机制:在Java中,对于一个类,首先,在编写代码的时候要写一个class xxx的定义,编译好就放在.class文件中,这是处于纸面的状态。此部分对应上边第一部分 —— 类型的构造;然后,Java会有一个Class对象,用于读取和表示这个纸面上的class xxx。此部分对应上边的第二部分 —— 类型的初始化;最终,Java生成真正的对象。此部分对应上边的第三部分 —— 对象的构造。

再结合实际流程解释一下:class_init函数会生成XXXClass,就相当于Java里边的Class对象;TypeImpl还会有一个instance_init函数,相当于构造函数,用于根据XXXClass生成Object,这就相当于Java反射中最终创建的对象。

欲知后事如何,且看下回分解。

举报

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