qemu参数解析

代码版本:qemu1.5

一、qemu有哪些参数

1、qemu-options.hx文件

qemu可用参数位于qemu-options.hx文件中,例如:

DEF("kernel", HAS_ARG, QEMU_OPTION_kernel, \
    "-kernel bzImage use 'bzImage' as kernel image\n", QEMU_ARCH_ALL)
STEXI
@item -kernel @var{bzImage}
@findex -kernel
Use @var{bzImage} as kernel image. The kernel can be either a Linux kernel
or in multiboot format.
ETEXI

用法:-kernel  bzImage,这是个很简单的命令。

DEF中的第一个参数对应"-kernel"去掉'-';第二个参数表示-kernel命令是有参数的;第三个参数是一个index,唯一的;第四个是help信息;第五个表示支持哪些平台。

DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
    "-machine [type=]name[,prop[=value][,...]]\n"
    "                selects emulated machine ('-machine help' for list)\n"
    "                property accel=accel1[:accel2[:...]] selects accelerator\n"
    "                supported accelerators are kvm, xen, tcg (default: tcg)\n"
    "                kernel_irqchip=on|off controls accelerated irqchip support\n"
    "                kvm_shadow_mem=size of KVM shadow MMU\n"
    "                dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
    "                mem-merge=on|off controls memory merge support (default: on)\n",
    QEMU_ARCH_ALL)
STEXI
@item -machine [type=]@var{name}[,prop=@var{value}[,...]]
@findex -machine
Select the emulated machine by @var{name}. Use @code{-machine help} to list
available machines. Supported machine properties are:
@table @option
@item accel=@var{accels1}[:@var{accels2}[:...]]
This is used to enable an accelerator. Depending on the target architecture,
kvm, xen, or tcg can be available. By default, tcg is used. If there is more
than one accelerator specified, the next one is used if the previous one fails
to initialize.
@item kernel_irqchip=on|off
Enables in-kernel irqchip support for the chosen accelerator when available.
@item kvm_shadow_mem=size
Defines the size of the KVM shadow MMU.
@item dump-guest-core=on|off
Include guest memory in a core dump. The default is on.
@item mem-merge=on|off
Enables or disables memory merge support. This feature, when supported by
the host, de-duplicates identical memory pages among VMs instances
(enabled by default).
@end table
ETEXI

用法:-machine [type=]@var{name}[,prop=@var{value}[,...]],这种命令的使用和解析就比较复杂了。

2、qemu-options-wrapper.h

qemu-options-wrapper.h会include qemu-options.hx,根据QEMU_OPTIONS_GENERATE_ENUM、QEMU_OPTIONS_GENERATE_HELP、QEMU_OPTIONS_GENERATE_OPTIONS是否定义,实现三种不同的功能,分别是enum,打印help信息和定义参数。

#if defined(QEMU_OPTIONS_GENERATE_ENUM)

#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask)     \
    opt_enum,
#define DEFHEADING(text)
#define ARCHHEADING(text, arch_mask)

#elif defined(QEMU_OPTIONS_GENERATE_HELP)

#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask)    \
    if ((arch_mask) & arch_type)                               \
        fputs(opt_help, stdout);

#define ARCHHEADING(text, arch_mask) \
    if ((arch_mask) & arch_type)    \
        puts(stringify(text));

#define DEFHEADING(text) ARCHHEADING(text, QEMU_ARCH_ALL)

#elif defined(QEMU_OPTIONS_GENERATE_OPTIONS)

#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask)     \
    { option, opt_arg, opt_enum, arch_mask },
#define DEFHEADING(text)
#define ARCHHEADING(text, arch_mask)

#else
#error "qemu-options-wrapper.h included with no option defined"
#endif

#include "qemu-options.def"

enum

enum {
#define QEMU_OPTIONS_GENERATE_ENUM
#include "qemu-options-wrapper.h"
};

打印help信息

static void help(int exitcode)
{
    version();
    printf("usage: %s [options] [disk_image]\n\n"
           "'disk_image' is a raw hard disk image for IDE hard disk 0\n\n",
            error_get_progname());

#define QEMU_OPTIONS_GENERATE_HELP
#include "qemu-options-wrapper.h"

    printf("\nDuring emulation, the following keys are useful:\n"
           "ctrl-alt-f      toggle full screen\n"
           "ctrl-alt-n      switch to virtual console 'n'\n"
           "ctrl-alt        toggle mouse and keyboard grab\n"
           "\n"
           "When using -nographic, press 'ctrl-a h' to get some help.\n");

    exit(exitcode);
}

定义命令

qemu_options数组保存了所有的合法的参数:

static const QEMUOption qemu_options[] = {
    { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },
#define QEMU_OPTIONS_GENERATE_OPTIONS
#include "qemu-options-wrapper.h"
    { NULL },
};

二、解析简单参数示例

以-kernel bzImage为例说明简单参数的解析

QemuOptsList

qemu所有解析后的命令行参数都保存在QemuOptsList的数组vm_config_groups中:

static QemuOptsList *vm_config_groups[32];

每个QemuOptsList中都记录了一类参数,比如machine类的参数:

static QemuOptsList qemu_machine_opts = {
    .name = "machine",
    .implied_opt_name = "type",
    .merge_lists = true,
    .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
    .desc = {
        {
            .name = "type",
            .type = QEMU_OPT_STRING,
            .help = "emulated machine"
        }, {
            .name = "accel",
            .type = QEMU_OPT_STRING,
            .help = "accelerator list",
        }, {
            .name = "kernel_irqchip",
            .type = QEMU_OPT_BOOL,
            .help = "use KVM in-kernel irqchip",
        }, {
            .name = "kvm_shadow_mem",
            .type = QEMU_OPT_SIZE,
            .help = "KVM shadow MMU size",
        }, {
            .name = "kernel",
            .type = QEMU_OPT_STRING,
            .help = "Linux kernel image file",
        }, {
            .name = "initrd",
            .type = QEMU_OPT_STRING,
            .help = "Linux initial ramdisk file",
        }, {
            .name = "append",
            .type = QEMU_OPT_STRING,
            .help = "Linux kernel command line",
        }, {
            .name = "dtb",
            .type = QEMU_OPT_STRING,
            .help = "Linux kernel device tree file",
        }, {
            .name = "dumpdtb",
            .type = QEMU_OPT_STRING,
            .help = "Dump current dtb to a file and quit",
        }, {
            .name = "phandle_start",
            .type = QEMU_OPT_STRING,
            .help = "The first phandle ID we may generate dynamically",
        }, {
            .name = "dt_compatible",
            .type = QEMU_OPT_STRING,
            .help = "Overrides the \"compatible\" property of the dt root node",
        }, {
            .name = "dump-guest-core",
            .type = QEMU_OPT_BOOL,
            .help = "Include guest memory in  a core dump",
        }, {
            .name = "mem-merge",
            .type = QEMU_OPT_BOOL,
            .help = "enable/disable memory merge support",
        },{
            .name = "usb",
            .type = QEMU_OPT_BOOL,
            .help = "Set on/off to enable/disable usb",
        },
        { /* End of list */ }
    },
};

向vm_config_groups中添加东西的代码:

    qemu_add_opts(&qemu_drive_opts);
    qemu_add_opts(&qemu_chardev_opts);
    qemu_add_opts(&qemu_device_opts);
    qemu_add_opts(&qemu_netdev_opts);
    qemu_add_opts(&qemu_net_opts);
    qemu_add_opts(&qemu_rtc_opts);
    qemu_add_opts(&qemu_global_opts);
    qemu_add_opts(&qemu_mon_opts);
    qemu_add_opts(&qemu_trace_opts);
    qemu_add_opts(&qemu_option_rom_opts);
    qemu_add_opts(&qemu_machine_opts);
    qemu_add_opts(&qemu_boot_opts);
    qemu_add_opts(&qemu_sandbox_opts);
    qemu_add_opts(&qemu_add_fd_opts);
    qemu_add_opts(&qemu_object_opts);
    qemu_add_opts(&qemu_tpmdev_opts);
    qemu_add_opts(&qemu_realtime_opts);

void qemu_add_opts(QemuOptsList *list)
{
    int entries, i;

    entries = ARRAY_SIZE(vm_config_groups);
    entries--; /* keep list NULL terminated */
    for (i = 0; i < entries; i++) {
        if (vm_config_groups[i] == NULL) {
            vm_config_groups[i] = list;
            return;
        }
    }
    fprintf(stderr, "ran out of space in vm_config_groups");
    abort();
}

QemuOptsList是QemuOpts的链表,QemuOptsList可以有多个QemuOpts,每一个使用QemuOpts中的id去区分,如果QemuOptsList中的merge_lists为true,那么只有一个QemuOpts。

QemuOpts是QemuOpt的链表,每一个QemuOpt记录了一个最基本的name,str(or value)。

-kernel bzImage被代码:

case QEMU_OPTION_kernel:
                qemu_opts_set(qemu_find_opts("machine"), 0, "kernel", optarg);
                break;

解析为:

vm_config_groups->QemuOptsList(为qemu_machine_opts)->QemuOpts(merge_lists=true,所以是唯一一个QemuOpts)->QemuOpt(name=kernel,str=bzImage)

先找到machine对应的QemuOptsList:

QemuOptsList *qemu_find_opts(const char *group)
{
    QemuOptsList *ret;
    Error *local_err = NULL;

    ret = find_list(vm_config_groups, group, &local_err);
    if (error_is_set(&local_err)) {
        error_report("%s", error_get_pretty(local_err));
        error_free(local_err);
    }

    return ret;
}

根据QemuOptsList的name来匹配:

static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
                               Error **errp)
{
    int i;

    for (i = 0; lists[i] != NULL; i++) {
        if (strcmp(lists[i]->name, group) == 0)
            break;
    }
    if (lists[i] == NULL) {
        error_set(errp, QERR_INVALID_OPTION_GROUP, group);
    }
    return lists[i];
}

QemuOpts

找到QemuOptsList后,先创建(or 查找)QemuOpts。然后在QemuOpts中插入QemuOpt,name为kernel,value为bzImage文件名:

int qemu_opts_set(QemuOptsList *list, const char *id,
                  const char *name, const char *value)
{
    QemuOpts *opts;
    Error *local_err = NULL;

    opts = qemu_opts_create(list, id, 1, &local_err);
    if (error_is_set(&local_err)) {
        qerror_report_err(local_err);
        error_free(local_err);
        return -1;
    }
    return qemu_opt_set(opts, name, value);
}

根据id创建(or 查找)QemuOpts。id_wellformed检查是否有非法字符,merge_lists表示QemuOptsList中的QemuOpts是否唯一:

QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
                           int fail_if_exists, Error **errp)
{
    QemuOpts *opts = NULL;

    if (id) {
        if (!id_wellformed(id)) {
            error_set(errp,QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
#if 0 /* conversion from qerror_report() to error_set() broke this: */
            error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n");
#endif
            return NULL;
        }
        opts = qemu_opts_find(list, id);
        if (opts != NULL) {
            if (fail_if_exists && !list->merge_lists) {
                error_set(errp, QERR_DUPLICATE_ID, id, list->name);
                return NULL;
            } else {
                return opts;
            }
        }
    } else if (list->merge_lists) {
        opts = qemu_opts_find(list, NULL);
        if (opts) {
            return opts;
        }
    }
    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;
}
struct QemuOpts {
    char *id;
    QemuOptsList *list;
    Location loc;
    QTAILQ_HEAD(QemuOptHead, QemuOpt) head;
    QTAILQ_ENTRY(QemuOpts) next;
};

QemuOpt

在QemuOpts中插入QemuOpt,name为kernel,value为bzImage文件名:

int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
{
    Error *local_err = NULL;

    opt_set(opts, name, value, false, &local_err);
    if (error_is_set(&local_err)) {
        qerror_report_err(local_err);
        error_free(local_err);
        return -1;
    }

    return 0;
}
static void opt_set(QemuOpts *opts, const char *name, const char *value,
                    bool prepend, Error **errp)
{
    QemuOpt *opt;
    const QemuOptDesc *desc;
    Error *local_err = NULL;

    desc = find_desc_by_name(opts->list->desc, name);
    if (!desc && !opts_accepts_any(opts)) {
        error_set(errp, QERR_INVALID_PARAMETER, name);
        return;
    }

    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);
    }
    opt->desc = desc;
    opt->str = g_strdup(value);
    qemu_opt_parse(opt, &local_err);
    if (error_is_set(&local_err)) {
        error_propagate(errp, local_err);
        qemu_opt_del(opt);
    }
}

看看desc中kernel对应value的类型是啥,字符串,bool or int:

static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc,
                                            const char *name)
{
    int i;

    for (i = 0; desc[i].name != NULL; i++) {
        if (strcmp(desc[i].name, name) == 0) {
            return &desc[i];
        }
    }

    return NULL;
}
static QemuOptsList qemu_machine_opts = {
    .name = "machine",
    .implied_opt_name = "type",
    .merge_lists = true,
    .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
    .desc = {
        // ...
        {
            .name = "kernel",
            .type = QEMU_OPT_STRING,
            .help = "Linux kernel image file",
        }
        // ...
        , { /* End of list */ }
    },
};

真正的插入QemuOpt的操作:

static void qemu_opt_parse(QemuOpt *opt, Error **errp)
{
    if (opt->desc == NULL)
        return;

    switch (opt->desc->type) {
    case QEMU_OPT_STRING:
        /* nothing */
        return;
    case QEMU_OPT_BOOL:
        parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp);
        break;
    case QEMU_OPT_NUMBER:
        parse_option_number(opt->name, opt->str, &opt->value.uint, errp);
        break;
    case QEMU_OPT_SIZE:
        parse_option_size(opt->name, opt->str, &opt->value.uint, errp);
        break;
    default:
        abort();
    }
}
struct QemuOpt {
    const char   *name;
    const char   *str;

    const QemuOptDesc *desc;
    union {
        bool boolean;
        uint64_t uint;
    } value;

    QemuOpts     *opts;
    QTAILQ_ENTRY(QemuOpt) next;
};

三、解析复杂参数示例

类似于这种参数: -aaa key1=value2,key2=value2,key3=value3。如果key1没有指定,那么就是implied_opt_name。将被解析为:

vm_config_groups->QemuOptsList->QemuOpts->QemuOpt(name=key1,str=value1)

->QemuOpt(name=key2,str=value2)

->QemuOpt(name=key3,str=value3)

复杂的参数由以下两种函数解析,params就是"key1=value2,key2=value2,key3=value3":

QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
                          int permit_abbrev)
{
    return opts_parse(list, params, permit_abbrev, false);
}

void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
                            int permit_abbrev)
{
    QemuOpts *opts;

    opts = opts_parse(list, params, permit_abbrev, true);
    assert(opts);
}

可能会使用解析到的id去创建QemuOpts,也可能使用默认的QemuOpts。得到QemuOpts之后,使用opts_do_parse去解析params:

static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
                            int permit_abbrev, bool defaults)
{
    const char *firstname;
    char value[1024], *id = NULL;
    const char *p;
    QemuOpts *opts;
    Error *local_err = NULL;

    assert(!permit_abbrev || list->implied_opt_name);
    firstname = permit_abbrev ? list->implied_opt_name : NULL;

    if (strncmp(params, "id=", 3) == 0) {
        get_opt_value(value, sizeof(value), params+3);
        id = value;
    } else if ((p = strstr(params, ",id=")) != NULL) {
        get_opt_value(value, sizeof(value), p+4);
        id = value;
    }
    if (defaults) {
        if (!id && !QTAILQ_EMPTY(&list->head)) {
            opts = qemu_opts_find(list, NULL);
        } else {
            opts = qemu_opts_create(list, id, 0, &local_err);
        }
    } else {
        opts = qemu_opts_create(list, id, 1, &local_err);
    }
    if (opts == NULL) {
        if (error_is_set(&local_err)) {
            qerror_report_err(local_err);
            error_free(local_err);
        }
        return NULL;
    }

    if (opts_do_parse(opts, params, firstname, defaults) != 0) {
        qemu_opts_del(opts);
        return NULL;
    }

    return opts;
}

逗号为分隔符,如果key1没有指定,那么就是QemuOptsList中的implied_opt_name。flag类的参数没有value,解析后添加on或者off,如果以no开头,会把name开头的no去掉,value设置为off。

static int opts_do_parse(QemuOpts *opts, const char *params,
                         const char *firstname, bool prepend)
{
    char option[128], value[1024];
    const char *p,*pe,*pc;
    Error *local_err = NULL;

    for (p = params; *p != '\0'; p++) {
        pe = strchr(p, '=');
        pc = strchr(p, ',');
        if (!pe || (pc && pc < pe)) {
            /* found "foo,more" */
            if (p == params && firstname) {
                /* implicitly named first option */
                pstrcpy(option, sizeof(option), firstname);
                p = get_opt_value(value, sizeof(value), p);
            } else {
                /* option without value, probably a flag */
                p = get_opt_name(option, sizeof(option), p, ',');
                if (strncmp(option, "no", 2) == 0) {
                    memmove(option, option+2, strlen(option+2)+1);
                    pstrcpy(value, sizeof(value), "off");
                } else {
                    pstrcpy(value, sizeof(value), "on");
                }
            }
        } else {
            /* found "foo=bar,more" */
            p = get_opt_name(option, sizeof(option), p, '=');
            if (*p != '=') {
                break;
            }
            p++;
            p = get_opt_value(value, sizeof(value), p);
        }
        if (strcmp(option, "id") != 0) {
            /* store and parse */
            opt_set(opts, option, value, prepend, &local_err);
            if (error_is_set(&local_err)) {
                qerror_report_err(local_err);
                error_free(local_err);
                return -1;
            }
        }
        if (*p != ',') {
            break;
        }
    }
    return 0;
}
时间: 2024-10-06 10:19:52

qemu参数解析的相关文章

使用ANTLR进行命令行参数解析

关于命令行参数的解析没有特定的规则,目前比较流行的有unix风格和微软风格.其实除了unix风格 的比较一致外,微软自己提供的命令行参数解析就有很多种风格.在.net平台下的main函数中,仅仅把参 数分解为以空格分割的数组,这对需要加开关,并且有的开关有自己的参数的情况是不够的,而且为了解 析这些参数需要学习部分词法分析的知识,这对用处不是很大的命令行参数显得有些"鸡肋",当然用 Antlr来处理命令行参数更显得有些鸡肋,并且是大才小用,因为Antlr的语法规则比较复杂,学习起来有

程序员的量化交易之路(11)--命令参数解析库JCommonder学习

转载须注明出处:http://blog.csdn.net/minimicall?viewmode=contents,http://cloudtrade.top 在学习量化交易平台的过程中,接触到一个参数解析的库,JCommander.今天把它记录一下. 它的官网为:http://www.jcommander.org/ 1. 概述 Jcommander是一个非常小的框架,用于解析命令行参数. 可以通过注解来描述你的参数选项: import com.beust.jcommander.Paramete

一个JavaScript函数把URL参数解析成Json对象_javascript技巧

问题:请编写一个JavaScript函数parseQueryString,它的用途是把URL参数解析为一个对象. eg:var obj=parseQueryString(url); 创建对象的三种形式: 一: var Person=new Object(); Person.name="Sun"; Person.age=24; 二: var Person=new Object(); Person["name"]="Sun"; Person[&quo

python命令行参数解析OptionParser类用法实例_python

本文实例讲述了python命令行参数解析OptionParser类的用法,分享给大家供大家参考. 具体代码如下: from optparse import OptionParser parser = OptionParser(usage="usage:%prog [optinos] filepath") parser.add_option("-t", "--timeout", action = "store", type =

一步一步自定义SpringMVC参数解析器

随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现HandlerMethodArgumentResolver接口,重写supportsParameter和resolveArgument方法,配置文件中加入resolver配置. 如果需要多个解析器同时生效需要在一个解析器中对其他解析器做兼容 缘起 为什么要自定义一个解析器呢? 源于需要对前端请求参数

佳能100D相机性能参数解析分享

给各位摄影爱好者们来详细的解析分享一下佳能100D相机的性能参数. 解析分享: 佳能100D采用一块104万像素3英寸的液晶触摸屏,可以通过直接触摸调节相机参数,背部的手柄处的蒙皮防滑处理操控起来很舒服.机身尺寸为116.8×90.7×69.4mm,重约370克(机身).   佳能100D搭载了1800万像素的APS-C画幅传感器和DIGIC 5图像处理器,常用感光度范围宽广,为ISO 100-12800,并可拓展至ISO 25600.佳能100D还搭载了多张拍摄降噪功能,可合成4张图像抑制噪点

Go语言中使用flag包对命令行进行参数解析的方法_Golang

flagflag 是Go 标准库提供的解析命令行参数的包. 使用方式: flag.Type(name, defValue, usage) 其中Type为String, Int, Bool等:并返回一个相应类型的指针. flag.TypeVar(&flagvar, name, defValue, usage) 将flag绑定到一个变量上. 自定义flag只要实现flag.Value接口即可: type Value interface { String() string Set(string) er

佳能5DMarkII相机性能参数解析

给各位摄影爱好者们来详细的解析分享一下佳能5DMarkII这一款相机的性能参数. 解析分享: 佳能5D Mark II使用最新的3.0英寸92万像素低温多晶硅TFT液晶监视,视野率100%,拥有7级亮度调节,以及3种倾向的自动亮度调节(偏暗.中性.偏亮). 佳能5D Mark II的机身使用了镁合金材料,轻巧而坚固,并具有出色的电磁屏蔽性能.机身尺寸为152×113.5×75mm,重量为810g.     佳能5D Mark II是全画幅单反相机,配备了2110万有效像素的全画幅CMOS图像感应

[20171105]exp imp buffer参数解析.txt

[20171105]exp imp buffer参数解析.txt oracle官方所给的关于buffer的解释如下: https://docs.oracle.com/cd/A84870_01/doc/server.816/a76955/ch01.htm BUFFER Default: operating system-dependent. See your Oracle operating system-specific documentation to determine the defaul