8.3.1参数初始化流程
其代码位于vl.c(main.c)
a) 判断是否使用default设置
optind = 1;
popt = lookup_opt(argc, argv,&optarg, &optind);
switch (popt->index) {
case QEMU_OPTION_nodefconfig:
defconfig= false;
break;
caseQEMU_OPTION_nouserconfig:
userconfig= false;
break;
}
b) 若使用default设置,则
if (defconfig)
ret =qemu_read_default_config_files(userconfig);
c) 针对不同命令行参数,做不同预处理,这里看看QEMU_OPTION_hda的流程
popt = lookup_opt(argc, argv,&optarg, &optind);
switch(popt->index) {
case QEMU_OPTION_hda:
{
charbuf[256];
if(cyls == 0)
snprintf(buf,sizeof(buf), "%s", HD_OPTS);
else
snprintf(buf,sizeof(buf), "%s,cyls=%d,heads=%d,secs=%d%s",
HD_OPTS, cyls, heads, secs,
translation== BIOS_ATA_TRANSLATION_LBA ?
",trans=lba":
translation== BIOS_ATA_TRANSLATION_NONE ?
",trans=none": "");
drive_add(IF_DEFAULT,0, optarg, buf);
break;
}
}
d) 参数的使用
if(qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,&machine->use_scsi, 1) != 0)
exit(1);
8.3.2 代码分析
(1) lookup_opt
optind = 1;
popt =lookup_opt(argc, argv, &optarg, &optind);
lookup_opt 负责从
a.argv中取出一个参数,并将optfind加1。
b.从qemu_options列表中根据参数名进行匹配
popt = qemu_options;
for(;;) {
if(!strcmp(popt->name, r + 1))
break;
popt++;
}
optarg = argv[optind++];
*poptarg = optarg;
*poptind = optind;
return popt;
static const QEMUOption qemu_options[] = {
{ "h", 0,QEMU_OPTION_h, QEMU_ARCH_ALL },
#define QEMU_OPTIONS_GENERATE_OPTIONS
#include "qemu-options-wrapper.h"
{ NULL },
};
查看qemu-options-wrapper.h发现qemu-options.def定义了每个参数项。
DEF("hda", HAS_ARG, QEMU_OPTION_hda,
"-hda/-hdb file use'file' as IDE hard disk 0/1 image\n", QEMU_ARCH_ALL)
(2) Default config加载
qemu_read_default_config_files ==》
for (f = default_config_files;f->filename; f++) {
if (!userconfig&& f->userconfig) {
continue;
}
ret =qemu_read_config_file(f->filename);
if (ret < 0&& ret != -ENOENT) {
return ret;
}
}
static struct defconfig_file {
const char *filename;
/* Indicates it is anuser config file (disabled by -no-user-config) */
bool userconfig;
} default_config_files[] = {
{ CONFIG_QEMU_CONFDIR"/qemu.conf", true },
{ CONFIG_QEMU_CONFDIR"/target-" TARGET_ARCH".conf", true },
{ NULL }, /* end oflist */
};
qemu_read_config_file ==》qemu_config_parse(f, vm_config_groups, filename);
从配置文件中解析参数并用qemu_opts_create和qemu_opt_set设置参数和其值
qemu_opts_create 将参数项添加到链表中,返回QemuOpts * opts:
QTAILQ_INIT(&opts->head);
QTAILQ_INSERT_TAIL(&list->head, opts, next);
lists为vm_config_groups,其每个子项时一个list
static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts,
。。。。。。。
NULL,
};
struct QemuOptsList {
const char *name;
const char*implied_opt_name;
bool merge_lists; /* Merge multiple uses of option into asingle list? */
QTAILQ_HEAD(,QemuOpts) head;
QemuOptDesc desc[];
};
static QemuOptsList qemu_drive_opts = {
.name ="drive",
.head =QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
.desc = {
{
.name ="bus",
.type =QEMU_OPT_NUMBER,
.help ="bus number",
},{
.name ="unit",
.type =QEMU_OPT_NUMBER,
.help ="unit number (i.e. lun for scsi)",
},{
.name ="if",
.type =QEMU_OPT_STRING,
.help ="interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
},{
.name ="index",
.type =QEMU_OPT_NUMBER,
.help ="index number",
},
.........
一个oplist有多个desc.用于描述不同的子参数设子
qemu_opt_set ==> opt_set(opts, name, value, false,&local_err);
根据opt的子参数,设置opt项的值
QemuOpt *opt;
opt =g_malloc0(sizeof(*opt));
opt->name =g_strdup(name);
opt->opts = opts;
if (prepend) {
QTAILQ_INSERT_HEAD(&opts->head, opt, next);
} else {
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
}
小结:(1) QemuOptsList *vm_config_groups 管理不同的配置类别;
(2) QemuOptsList->head 链表管理用于添加的QemuOpts *opts;
(3) QemuOpts->head链表管理子项设置QemuOpt * opt
(4) 每个QemuOpt ->desc 记录了它对应在opts中的那个子项参数
(3) driver 参数分析
QemuOpts *drive_add(BlockInterfaceType type, int index, constchar *file,
constchar *optstr)
{
opts =drive_def(optstr); //取QemuOpts *opts
if (type !=IF_DEFAULT) {
qemu_opt_set(opts,"if", if_name[type]);
}
if (index >= 0) {
snprintf(buf,sizeof(buf), "%d", index);
qemu_opt_set(opts,"index", buf);
}
if (file)
qemu_opt_set(opts,"file", file);
return opts;
}
drive_def ==》 qemu_opts_parse(qemu_find_opts("drive"),optstr, 0);
a. qemu_find_opts("drive") 根据名称得到vm_config_groups中的数组一项qemu_drive_opts;
b. qemu_opts_parse ==> opts_parse 在QemuOptsList的链表中搜索QemuOpts *opts,若不存在则创建。
qemu_opts_foreach(qemu_find_opts("drive"),drive_init_func, &machine->use_scsi, 1);
qemu_opts_foreach 遍历QemuOptsList, 的每个子项QemuOpts *opts, 并为其调用callback:
typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque);
drive_init_func ==> drive_init: 取出参数的值
bus_id = qemu_opt_get_number(opts, "bus",0);
unit_id =qemu_opt_get_number(opts, "unit", -1);
index = qemu_opt_get_number(opts,"index", -1);