接前一篇文章: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结构并进行初始化。