接前一篇文章: QEMU源码全解析5 —— QEMU参数解析(5)
本文内容参考:
《趣谈 Linux 操作系统》 —— 刘超,极客时间
《 QEMU /KVM》源码解析与应用 —— 李强,机械工业出版社
特此致谢!
上一回说到了qemu_opts_parse_noisily函数只是简单调用了opt_parse函数,opt_parse函数解析出一个QemuOpts。本文对opt_parse函数进行详解。
为了便于理解,先再贴一下qemu_opts_parse_noisily函数代码,在util/qemu-option.c中,代码如下:
- /**
- * Create a QemuOpts in @list and with options parsed from @params.
- * If @permit_abbrev, the first key=value in @params may omit key=,
- * and is treated as if key was @list->implied_opt_name.
- * Report errors with error_report_err(). This is inappropriate in
- * QMP context. Do not use this function there!
- * Return the new QemuOpts on success, null pointer on error.
- */
- QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
- bool permit_abbrev)
- {
- Error *err = NULL;
- QemuOpts *opts;
- bool help_wanted = false;
-
- opts = opts_parse(list, params, permit_abbrev, true,
- opts_accepts_any(list) ? NULL : &help_wanted,
- &err);
- if (!opts) {
- assert(!!err + !!help_wanted == 1);
- if (help_wanted) {
- qemu_opts_print_help(list, true);
- } else {
- error_report_err(err);
- }
- }
- return opts;
- }
opts_parse函数在util/qemu-option.c中,代码如下:
- static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
- bool permit_abbrev,
- bool warn_on_flag, bool *help_wanted, Error **errp)
- {
- const char *firstname;
- char *id = opts_parse_id(params);
- QemuOpts *opts;
-
- assert(!permit_abbrev || list->implied_opt_name);
- firstname = permit_abbrev ? list->implied_opt_name : NULL;
-
- opts = qemu_opts_create(list, id, !list->merge_lists, errp);
- g_free(id);
- if (opts == NULL) {
- return NULL;
- }
-
- if (!opts_do_parse(opts, params, firstname,
- warn_on_flag, help_wanted, errp)) {
- qemu_opts_del(opts);
- return NULL;
- }
-
- return opts;
- }
opts_parse函数调用的最重要的两个函数是qemu_opts_create和opts_do_parse。一个一个来看。
qemu_opts_create函数在util/qemu-option.c中,代码如下:
- QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
- int fail_if_exists, Error **errp)
- {
- QemuOpts *opts = NULL;
-
- if (list->merge_lists) {
- if (id) {
- error_setg(errp, QERR_INVALID_PARAMETER, "id");
- return NULL;
- }
- opts = qemu_opts_find(list, NULL);
- if (opts) {
- return opts;
- }
- } else if (id) {
- assert(fail_if_exists);
- if (!id_wellformed(id)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id",
- "an identifier");
- error_append_hint(errp, "Identifiers consist of letters, digits, "
- "'-', '.', '_', starting with a letter.\n");
- return NULL;
- }
- opts = qemu_opts_find(list, id);
- if (opts != NULL) {
- error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
- return NULL;
- }
- }
- opts = g_malloc0(sizeof(*opts));
- opts->id = g_strdup(id);
- opts->list = list;
- loc_save(&opts->loc);
- QTAILQ_INIT(&opts->head);
- QTAILQ_INSERT_TAIL(&list->head, opts, next);
- return opts;
- }
qemu_opts_create用来创建opts并将其插入到QemuOptsList上。
opts_do_parse函数同样在util/qemu-option.c中,代码如下:
- static bool opts_do_parse(QemuOpts *opts, const char *params,
- const char *firstname,
- bool warn_on_flag, bool *help_wanted, Error **errp)
- {
- char *option, *value;
- const char *p;
- QemuOpt *opt;
-
- for (p = params; *p;) {
- p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);
- if (help_wanted && *help_wanted) {
- g_free(option);
- g_free(value);
- return false;
- }
- firstname = NULL;
-
- if (!strcmp(option, "id")) {
- g_free(option);
- g_free(value);
- continue;
- }
-
- opt = opt_create(opts, option, value);
- g_free(option);
- if (!opt_validate(opt, errp)) {
- qemu_opt_del(opt);
- return false;
- }
- }
-
- return true;
- }
opts_do_parse函数的作用就是就是开始解析出一个一个的QemuOpt,解析出参数的值。QEMU的参数可以有多种情况,例如:foo,bar中foo表示开启一个flag;而另一种形式为foo=bar。对于这种情况,opts_do_parse需要处理各种可能的情况,并对每个值生成一个QemuOpt。下边以这个例子具体说明。
opts_do_parse函数首先根据各种情况(foo,bar或者foo=bar,more)解析出option以及value;然后调用opt_create,在该函数中会分配一个QemuOpt结构并进行初始化。