qemu参数解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 代码版本:qemu1.5 一、qemu有哪些参数 1、qemu-options.hx文件 qemu可用参数位于qemu-options.hx文件中,例如: DEF("kernel", HAS_ARG, QEMU_OPTION_kernel, \...

代码版本: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;
}


目录
相关文章
|
4月前
|
Java
解析Java线程池:参数详解与执行流程
解析Java线程池:参数详解与执行流程
46 1
|
5月前
|
SQL 存储 NoSQL
实时计算 Flink版产品使用合集之使用ParameterTool.fromArgs(args)解析参数为null,该怎么处理
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
13天前
|
JSON API 数据格式
requests库中json参数与data参数使用方法的深入解析
选择 `data`或 `json`取决于你的具体需求,以及服务器端期望接收的数据格式。
60 2
|
23天前
|
KVM 虚拟化
计算虚拟化之CPU——qemu解析
【9月更文挑战10天】本文介绍了QEMU命令行参数的解析过程及其在KVM虚拟化中的应用。展示了QEMU通过多个`qemu_add_opts`函数调用处理不同类型设备和配置选项的方式,并附上了OpenStack生成的一个复杂KVM参数实例。
|
2月前
|
C# 开发者 Windows
震撼发布:全面解析WPF中的打印功能——从基础设置到高级定制,带你一步步实现直接打印文档的完整流程,让你的WPF应用程序瞬间升级,掌握这一技能,轻松应对各种打印需求,彻底告别打印难题!
【8月更文挑战第31天】打印功能在许多WPF应用中不可或缺,尤其在需要生成纸质文档时。WPF提供了强大的打印支持,通过`PrintDialog`等类简化了打印集成。本文将详细介绍如何在WPF应用中实现直接打印文档的功能,并通过具体示例代码展示其实现过程。
133 0
|
2月前
|
存储 Go UED
精通Go语言的命令行参数解析
【8月更文挑战第31天】
25 0
|
2月前
|
安全 数据安全/隐私保护
|
2月前
|
运维 监控 Java
【JVM 调优秘籍】实战指南:JVM 调优参数全解析,让 Java 应用程序性能飙升!
【8月更文挑战第24天】本文通过一个大型在线零售平台的例子,深入探讨了Java虚拟机(JVM)性能调优的关键技术。面对应用响应延迟的问题,文章详细介绍了几种常用的JVM参数调整策略,包括堆内存大小、年轻代配置、垃圾回收器的选择及日志记录等。通过具体实践(如设置`-Xms`, `-Xmx`, `-XX:NewRatio`, `-XX:+UseParallelGC`等),成功降低了高峰期的响应时间,提高了系统的整体性能与稳定性。案例展示了合理配置JVM参数的重要性及其对解决实际问题的有效性。
55 0
|
3月前
|
JSON API 数据处理
深度解析京东商品列表数据接口:功能、参数与实战技巧
京东商品列表数据接口让开发者通过HTTP请求获取京东商品详尽列表信息,包括ID、名称、价格等。接口支持参数化搜索(关键词、价格区间等),返回JSON格式数据,便于处理与分析。开发者需注册账号并创建应用以获取访问权限。应用场景涵盖市场调研、商品管理和营销策略制定等,有效提升数据驱动决策能力。
|
2月前
|
消息中间件 安全 RocketMQ
就软件研发问题之ACL 2.0接口不同的授权参数解析的问题如何解决
就软件研发问题之ACL 2.0接口不同的授权参数解析的问题如何解决

推荐镜像

更多
下一篇
无影云桌面