QEMU源码全解析14 —— QOM介绍(3)

88 篇文章 19 订阅
本文继续上篇关于QEMU中QOM的介绍,重点关注QEMU主函数启动过程中调用`module_call_init(MODULE_INIT_QOM)`的时机。通过源码分析,确定了调用路径:`main()` -> `qemu_main()` -> `qemu_init()` -> `qemu_init_subsystems()` -> `module_call_init(MODULE_INIT_QOM)`。同时,介绍了`pci_edu_register_types`函数如何注册类型并调用`type_register_static`。
摘要由CSDN通过智能技术生成

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

本文内容参考:

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

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

特此致谢!

上回书讲到了QOM中第一部分——类型的注册,讲了一半,本文接着往下讲。

进入 QEMU 的main函数不久,就以MODULE_INIT_QOM为参数调用了函数module_call_init(也是上文书讲过的)。那么具体是什么时候调用的module_call_init(MODULE_INIT_QOM)?在QEMU源码qemu-7.1.0(7.1.0版本源码)中搜索此关键字,会有很多地方,那么哪一个才是main函数不久后调用到的?

QEMU源码全解析7 —— QEMU主函数 中我曾经讲过main函数,是在softmmu/main.c中,为了便于理解和加深印象,再次贴出源码:

  1. int qemu_main(int argc, char **argv, char **envp)
  2. {
  3. int status;
  4. qemu_init(argc, argv, envp);
  5. status = qemu_main_loop();
  6. qemu_cleanup();
  7. return status;
  8. }
  9. #ifndef CONFIG_COCOA
  10. int main(int argc, char **argv)
  11. {
  12. return qemu_main(argc, argv, NULL);
  13. }
  14. #endif

主函数所在的位置是在softmmu目录下,那么理论上应该从上述关键字搜索结果中找到同目录下的那个module_call_init(MODULE_INIT_QOM),它最有可能是我们要找的。在softmmu目录下的runstate.c中确实有一处调用,代码如下:

  1. void qemu_init_subsystems(void)
  2. {
  3. Error *err = NULL;
  4. os_set_line_buffering();
  5. module_call_init(MODULE_INIT_TRACE);
  6. qemu_init_cpu_list();
  7. qemu_init_cpu_loop();
  8. qemu_mutex_lock_iothread();
  9. atexit(qemu_run_exit_notifiers);
  10. module_call_init(MODULE_INIT_QOM);
  11. module_call_init(MODULE_INIT_MIGRATION);
  12. runstate_init();
  13. precopy_infrastructure_init();
  14. postcopy_infrastructure_init();
  15. monitor_init_globals();
  16. if (qcrypto_init(&err) < 0) {
  17. error_reportf_err(err, "cannot initialize crypto: ");
  18. exit(1);
  19. }
  20. os_setup_early_signal_handling();
  21. bdrv_init_with_whitelist();
  22. socket_init();
  23. }

是在qemu_init_subsystems函数中调用了module_call_init(MODULE_INIT_QOM);。而qemu_init_subsystems函数又是在qemu_init函数中被调用的,代码片段如下(softmmu/vl.c中):

  1. void qemu_init(int argc, char **argv, char **envp)
  2. {
  3. QemuOpts *opts;
  4. QemuOpts *icount_opts = NULL, *accel_opts = NULL;
  5. QemuOptsList *olist;
  6. int optind;
  7. const char *optarg;
  8. MachineClass *machine_class;
  9. bool userconfig = true;
  10. FILE *vmstate_dump_file = NULL;
  11. qemu_add_opts(&qemu_drive_opts);
  12. qemu_add_drive_opts(&qemu_legacy_drive_opts);
  13. qemu_add_drive_opts(&qemu_common_drive_opts);
  14. qemu_add_drive_opts(&qemu_drive_opts);
  15. qemu_add_drive_opts(&bdrv_runtime_opts);
  16. ……
  17. qemu_init_subsystems();
  18. ……
  19. }

这就很清楚了,函数调用链为:main() -> qemu_main() -> qemu_init() -> qemu_init_subsystems() -> module_call_init(MODULE_INIT_QOM)。

弄清楚这一点之后,回到主题。为便于理解,再贴一下module_call_init函数代码(util/module.c中):

  1. void module_call_init(module_init_type type)
  2. {
  3. ModuleTypeList *l;
  4. ModuleEntry *e;
  5. if (modules_init_done[type]) {
  6. return;
  7. }
  8. l = find_type(type);
  9. QTAILQ_FOREACH(e, l, node) {
  10. e->init();
  11. }
  12. modules_init_done[type] = true;
  13. }

module_call_int函数执行了init_type_list[MODULE_INIT_QOM]链表上每一个ModuleEntry的init函数。

仍然以edu为例,该类型的init函数是pci_edu_register_types(前文中讲到的hw/misc/edu.c中的一句:type_init(pci_edu_register_types)),该函数在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)

pci_edu_register_types函数唯一的工作是构造了一个TypeInfo类型的edu_info,并将其作为参数调用了type_register_static。

更多细节和分析请看下回。

举报

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