QEMU源码全解析5 —— QEMU参数解析(5)

88 篇文章 19 订阅
本文详细探讨了QEMU参数解析过程,特别是以-device参数为例,分析了在softmmu/vl.c的main函数中的qemu_init函数如何处理参数。通过查找lookup_opt函数和qemu_opts_parse_noisily以及qemu_find_opts的源码,揭示了QEMU如何解析和应用设备选项。后续将深入解析opt_parse函数的工作原理。
摘要由CSDN通过智能技术生成

接前一篇文章: QEMU源码全解析4 —— QEMU参数解析(4)

本文内容参考:

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

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

特此致谢!

本篇文章以-device参数项为例简单分析QEMU参数的处理过程。

softmmu/vl.c的main函数(qemu_init函数)中有一个很长的循环来解析参数,代码片段如下所示:

  1. /* second pass of option parsing */
  2. optind = 1;
  3. for(;;) {
  4. if (optind >= argc)
  5. break;
  6. if (argv[optind][0] != '-') {
  7. loc_set_cmdline(argv, optind, 1);
  8. drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
  9. } else {
  10. const QEMUOption *popt;
  11. popt = lookup_opt(argc, argv, &optarg, &optind);
  12. if (!(popt->arch_mask & arch_type)) {
  13. error_report("Option not supported for this target");
  14. exit(1);
  15. }
  16. switch(popt->index) {
  17. case QEMU_OPTION_cpu:
  18. /* hw initialization will check this */
  19. cpu_option = optarg;
  20. break;
  21. case QEMU_OPTION_hda:
  22. case QEMU_OPTION_hdb:
  23. case QEMU_OPTION_hdc:
  24. case QEMU_OPTION_hdd:
  25. drive_add(IF_DEFAULT, popt->index - QEMU_OPTION_hda, optarg,
  26. HD_OPTS);
  27. break;
  28. case QEMU_OPTION_blockdev:
  29. {
  30. Visitor *v;
  31. BlockdevOptionsQueueEntry *bdo;
  32. v = qobject_input_visitor_new_str(optarg, "driver",
  33. &error_fatal);
  34. bdo = g_new(BlockdevOptionsQueueEntry, 1);
  35. visit_type_BlockdevOptions(v, NULL, &bdo->bdo,
  36. &error_fatal);
  37. visit_free(v);
  38. loc_save(&bdo->loc);
  39. QSIMPLEQ_INSERT_TAIL(&bdo_queue, bdo, entry);
  40. break;
  41. }
  42. case QEMU_OPTION_drive:
  43. opts = qemu_opts_parse_noisily(qemu_find_opts("drive"),
  44. optarg, false);
  45. if (opts == NULL) {
  46. exit(1);
  47. }
  48. break;
  49. ……
  50. case QEMU_OPTION_msg:
  51. opts = qemu_opts_parse_noisily(qemu_find_opts("msg"), optarg,
  52. false);
  53. if (!opts) {
  54. exit(1);
  55. }
  56. configure_msg(opts);
  57. break;
  58. case QEMU_OPTION_dump_vmstate:
  59. if (vmstate_dump_file) {
  60. error_report("only one '-dump-vmstate' "
  61. "option may be given");
  62. exit(1);
  63. }
  64. vmstate_dump_file = fopen(optarg, "w");
  65. if (vmstate_dump_file == NULL) {
  66. error_report("open %s: %s", optarg, strerror(errno));
  67. exit(1);
  68. }
  69. break;
  70. case QEMU_OPTION_enable_sync_profile:
  71. qsp_enable();
  72. break;
  73. case QEMU_OPTION_nouserconfig:
  74. /* Nothing to be parsed here. Especially, do not error out below. */
  75. break;
  76. default:
  77. if (os_parse_cmd_args(popt->index, optarg)) {
  78. error_report("Option not supported in this build");
  79. exit(1);
  80. }
  81. }
  82. }
  83. }

从注释中可以看到,是第2次选项解析,那么第1次解析是什么?其实就是本系列第2篇文章中讲到的lookup_opt函数,其在主函数中的代码片段如下:

  1. /* first pass of option parsing */
  2. optind = 1;
  3. while (optind < argc) {
  4. if (argv[optind][0] != '-') {
  5. /* disk image */
  6. optind++;
  7. } else {
  8. const QEMUOption *popt;
  9. popt = lookup_opt(argc, argv, &optarg, &optind);
  10. switch (popt->index) {
  11. case QEMU_OPTION_nouserconfig:
  12. userconfig = false;
  13. break;
  14. }
  15. }
  16. }
  17. machine_opts_dict = qdict_new();
  18. if (userconfig) {
  19. qemu_read_default_config_file(&error_fatal);
  20. }

为例便于理解,再贴一下lookup_opt函数代码,在softmmu/vl.c中:

  1. static const QEMUOption *lookup_opt(int argc, char **argv,
  2. const char **poptarg, int *poptind)
  3. {
  4. const QEMUOption *popt;
  5. int optind = *poptind;
  6. char *r = argv[optind];
  7. const char *optarg;
  8. loc_set_cmdline(argv, optind, 1);
  9. optind++;
  10. /* Treat --foo the same as -foo. */
  11. if (r[1] == '-')
  12. r++;
  13. popt = qemu_options;
  14. for(;;) {
  15. if (!popt->name) {
  16. error_report("invalid option");
  17. exit(1);
  18. }
  19. if (!strcmp(popt->name, r + 1))
  20. break;
  21. popt++;
  22. }
  23. if (popt->flags & HAS_ARG) {
  24. if (optind >= argc) {
  25. error_report("requires an argument");
  26. exit(1);
  27. }
  28. optarg = argv[optind++];
  29. loc_set_cmdline(argv, optind - 2, 2);
  30. } else {
  31. optarg = NULL;
  32. }
  33. *poptarg = optarg;
  34. *poptind = optind;
  35. return popt;
  36. }

还是回到第2次选项解析中来。这个循环用很长都不够表达了,那是“相当的长”,有1000行左右。这个循环中包含了一个一个的选项,其中就包括-device。当解析到“-device”时,对应的分支处理代码如下:

  1. case QEMU_OPTION_device:
  2. if (optarg[0] == '{') {
  3. QObject *obj = qobject_from_json(optarg, &error_fatal);
  4. DeviceOption *opt = g_new0(DeviceOption, 1);
  5. opt->opts = qobject_to(QDict, obj);
  6. loc_save(&opt->loc);
  7. assert(opt->opts != NULL);
  8. QTAILQ_INSERT_TAIL(&device_opts, opt, next);
  9. } else {
  10. if (!qemu_opts_parse_noisily(qemu_find_opts("device"),
  11. optarg, true)) {
  12. exit(1);
  13. }
  14. }
  15. break;

核心的代码为:!qemu_opts_parse_noisily(qemu_find_opts("device"), optarg, true))。其中包括2个函数,qemu_opts_parse_noisily和qemu_find_opts。

先来看后者。qemu_find_opts函数在util/qemu-config.c中,代码如下:

  1. QemuOptsList *qemu_find_opts(const char *group)
  2. {
  3. QemuOptsList *ret;
  4. Error *local_err = NULL;
  5. ret = find_list(vm_config_groups, group, &local_err);
  6. if (local_err) {
  7. error_report_err(local_err);
  8. }
  9. return ret;
  10. }

find_list函数就在qemu_find_opts函数上边,代码如下:

  1. static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
  2. Error **errp)
  3. {
  4. int i;
  5. qemu_load_module_for_opts(group);
  6. for (i = 0; lists[i] != NULL; i++) {
  7. if (strcmp(lists[i]->name, group) == 0)
  8. break;
  9. }
  10. if (lists[i] == NULL) {
  11. error_setg(errp, "There is no option group '%s'", group);
  12. }
  13. return lists[i];
  14. }

简单来讲,qemu_find_opts函数从全局变量vm_config_groups中找到刚才插入的device QemuOptsList并返回。

再来看前者,即qemu_opts_parse_noisily函数。其在util/qemu-option.c中实现,代码如下:

  1. /**
  2. * Create a QemuOpts in @list and with options parsed from @params.
  3. * If @permit_abbrev, the first key=value in @params may omit key=,
  4. * and is treated as if key was @list->implied_opt_name.
  5. * Report errors with error_report_err(). This is inappropriate in
  6. * QMP context. Do not use this function there!
  7. * Return the new QemuOpts on success, null pointer on error.
  8. */
  9. QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
  10. bool permit_abbrev)
  11. {
  12. Error *err = NULL;
  13. QemuOpts *opts;
  14. bool help_wanted = false;
  15. opts = opts_parse(list, params, permit_abbrev, true,
  16. opts_accepts_any(list) ? NULL : &help_wanted,
  17. &err);
  18. if (!opts) {
  19. assert(!!err + !!help_wanted == 1);
  20. if (help_wanted) {
  21. qemu_opts_print_help(list, true);
  22. } else {
  23. error_report_err(err);
  24. }
  25. }
  26. return opts;
  27. }

qemu_opts_parse_noisily函数只是简单调用了opt_parse函数,后者解析出一个QemuOpts。预知opt_parse函数的详情,且看下回分解。

举报

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