QEMU的参数解析
QEMU中定义了QEMUOption结构体来表示执行qemu-system-i386等命令时用到的选项。
并且在vl.c中定义了QEMUOption数组qemu_options来存储所有可用的选项,并且利用qemu-options-wrapper.h来给这个数组赋值。如上图
,#define QEMU_OPTIONS_GENERATE_OPTIONS选择qemu-options-wrapper.h的操作,在qemu-options-wrapper.h中有一些宏
QEMU_OPTIONS_GENERATE_ENUM: 生成一个枚举值列表,对应与上图中QEMUOption结构的index值,例如QEMU_OPTION_h
QEMU_OPTIONS_GENERATE_HELP: 生成帮助信息输出到标准输出
QEMU_OPTIONS_GENERATE_OPTIONS: 生成一组选项列表
在qemu-options.hx中可以看到调用,例如object选项
给qemu_options数组赋值之后,在vl.c的main函数中根据这个数组来解析命令。
参数解析第一步,遍历参数数组,通过lookup_opt函数来得到一个QEMUOption,判断这个QEMUOption是否在原来生成的QEMUOption数组中,主要作用是排错。第二步才是真正的解析
lookup_opt中通过参数名来在qemu_options这个全局数组中根据参数寻找对应的option,然后将option后边的参数保存到optarg
第二步也是遍历参数数组,通过lookup_opt函数找到对应的QEMUOption,然后将option后边的参数保存到optarg通过index成员来判断是哪个option,然后执行不同的分支。
解析参数用到的四个结构:QemuOpts, QemuOpt, QemuOptsList, QemuOptDesc
QemuOpts和QemuOpt定义在option_int.h中,QemuOpt存储子选项,每个QemuOpt都有一个QemuOptDesc来描述子选项的名字,类型以及帮助信息。QemuOpts表示同一个id下的所有子选项,QEMU可能会多次使用同一个大选项来指定多个相同的设备,这个就是用id来区分。
子选项的类型如下:
QemuOptDesc和QemuOptsList定义在option.h中,一个QemuOptsList代表一个qemu option,子选项就存在QemuOpt中。
整个QEMU选项结构体之间的关系图如下:
以object选项为例,具体解析
首先在vl.c的main函数中解析参数,解析参数用到的函数时qemu_opts_parse_noisily和qemu_find_opts
![
1.qemu_find_opts函数qemu_find_opts声明在qemu\config-file.h中,定义在qemu-config.c中,内部调用了find_list函数,find_list函数里面通过比较vm_config_groups(是一个QemuOptsList结构数组)的name与group的值,来返回一个QemuOptsList
vm_config_groups这个结构的填充,实在vl.c的main函数中通过调用qemu_add_opts这个函数把定义好的不同的选项QemuOptsList结构添加到这个数组中。
2.qemu_opts_parse_noisily函数
qemu_opts_parse_noisily声明在qemu\option.h中,定义在util\qemu-option.c中,调用了opts_parse
在opts_parse函数中首先获取id的值,然后调用qemu_opts_create返回一个opts,调用opts_do_parse解析。firstname是list->implied_opt_name值。
在qemu_opts_create中,判断如果id存在或list->merge_lists存在,则直接调用qemu_opts_find来返回一个QEMUOpts结构。否则新建一个QemuOpts节点,插入list。
在qemu_opts_find中,调用宏QTAILQ_FOREACH,遍历list中的每个QemuOpts节点,判断id是否同时存在或同事不存在,
opts_do_parse函数中,具体的解析操作
得到一个子选项后,调用opt_set函数,首先得到这个子选项的描述信息desc,然后新建一个子选项节点QemuOpt,填充信息,将这个QemuOpt节点加入到QemuOpts这个结构中。opt->str先保存子选项的值,然后调用qemu_opt_parse函数,根据子选项的类型来给value重新赋值。
在qemu_opt_parse中根据子选项的类型,重新对value赋值。