【QEMU系统分析之启动篇(十五)】

系列文章目录

第十五章 QEMU系统仿真的导出虚拟机分析



前言

本文以 QEMU 8.2.2 为例,分析其作为系统仿真工具的启动过程,并为读者展示各种 QEMU 系统仿真的启动配置实例。
本文读者需要具备一定的 QEMU 系统仿真使用经验,并对 C 语言编程有一定了解。


一、QEMU是什么?

QEMU 是一个通用且开源的机器模拟器和虚拟机。
其官方主页是:https://www.qemu.org/


二、QEMU系统仿真的启动分析

1.系统仿真的初始化代码

QEMU 作为系统仿真工具,其入口代码在 system/main.c 文件中,初始化函数 qemu_init() 的实现在 system/vl.c 文件中,在完成 QEMU 虚拟机的内存设备及NUMA初始化后,系统将处理导出信息的设置,本篇文章将完成以下代码部分的分析。

2.主循环数据初始化

这部分代码在 system/vl.c 文件中,实现如下:

void qemu_init(int argc, char **argv)
{
...
    if (vmstate_dump_file) {
        /* dump and exit */
        module_load_qom_all();
        dump_vmstate_json_to_file(vmstate_dump_file);
        exit(0);
    }
...
}

3. module_load_qom_all()

此函数在 /util/module.c 文件中,定义如下:

void module_load_qom_all(void)
{
    const QemuModinfo *modinfo;
    Error *local_err = NULL;

    if (module_loaded_qom_all) {
        return;
    }

    for (modinfo = module_info; modinfo->name != NULL; modinfo++) {
        if (!modinfo->objs) {
            continue;
        }
        if (!module_check_arch(modinfo)) {
            continue;
        }
        if (module_load("", modinfo->name, &local_err) < 0) {
            error_report_err(local_err);
        }
    }
    module_loaded_qom_all = true;
}

module_check_arch()

函数 module_check_arch() 定义如下:

static bool module_check_arch(const QemuModinfo *modinfo)
{
    if (modinfo->arch) {
        if (!module_arch) {
            /* no arch set -> ignore all */
            return false;
        }
        if (strcmp(module_arch, modinfo->arch) != 0) {
            /* mismatch */
            return false;
        }
    }
    return true;
}


module_load()

函数 module_load(),定义如下:

int module_load(const char *prefix, const char *name, Error **errp)
{
    int rv = -1;
#ifdef CONFIG_MODULE_UPGRADES
    char *version_dir;
#endif
    const char *search_dir;
    char *dirs[5];
    char *module_name;
    int i = 0, n_dirs = 0;
    bool export_symbols = false;
    static GHashTable *loaded_modules;
    const QemuModinfo *modinfo;
    const char **sl;

    if (!g_module_supported()) {
        error_setg(errp, "%s", "this platform does not support GLib modules");
        return -1;
    }

    if (!loaded_modules) {
        loaded_modules = g_hash_table_new(g_str_hash, g_str_equal);
    }

    /* allocate all resources managed by the out: label here */
    module_name = g_strdup_printf("%s%s", prefix, name);

    if (g_hash_table_contains(loaded_modules, module_name)) {
        g_free(module_name);
        return 2; /* module already loaded */
    }
    g_hash_table_add(loaded_modules, module_name);

    search_dir = getenv("QEMU_MODULE_DIR");
    if (search_dir != NULL) {
        dirs[n_dirs++] = g_strdup_printf("%s", search_dir);
    }
    dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR);

#ifdef CONFIG_MODULE_UPGRADES
    version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION),
                             G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~",
                             '_');
    dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir);
#endif
    assert(n_dirs <= ARRAY_SIZE(dirs));

    /* end of resources managed by the out: label */

    for (modinfo = module_info; modinfo->name != NULL; modinfo++) {
        if (modinfo->arch) {
            if (strcmp(modinfo->name, module_name) == 0) {
                if (!module_check_arch(modinfo)) {
                    error_setg(errp, "module arch does not match: "
                        "expected '%s', got '%s'", module_arch, modinfo->arch);
                    goto out;
                }
            }
        }
        if (modinfo->deps) {
            if (strcmp(modinfo->name, module_name) == 0) {
                /* we depend on other module(s) */
                for (sl = modinfo->deps; *sl != NULL; sl++) {
                    int subrv = module_load("", *sl, errp);
                    if (subrv <= 0) {
                        rv = subrv;
                        goto out;
                    }
                }
            } else {
                for (sl = modinfo->deps; *sl != NULL; sl++) {
                    if (strcmp(module_name, *sl) == 0) {
                        /* another module depends on us */
                        export_symbols = true;
                    }
                }
            }
        }
    }

    for (i = 0; i < n_dirs; i++) {
        char *fname = g_strdup_printf("%s/%s%s",
                                      dirs[i], module_name, CONFIG_HOST_DSOSUF);
        int ret = access(fname, F_OK);
        if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) {
            /*
             * if we don't find the module in this dir, try the next one.
             * If we don't find it in any dir, that can be fine too: user
             * did not install the module. We will return 0 in this case
             * with no error set.
             */
            g_free(fname);
            continue;
        } else if (ret != 0) {
            /* most common is EACCES here */
            error_setg_errno(errp, errno, "error trying to access %s", fname);
        } else if (module_load_dso(fname, export_symbols, errp)) {
            rv = 1; /* module successfully loaded */
        }
        g_free(fname);
        goto out;
    }
    rv = 0; /* module not found */

out:
    if (rv <= 0) {
        g_hash_table_remove(loaded_modules, module_name);
        g_free(module_name);
    }
    for (i = 0; i < n_dirs; i++) {
        g_free(dirs[i]);
    }
    return rv;
}

函数 module_load(),定义如下:

/* Load a module from a DSO */
static CONF_MODULE *module_load_dso(const CONF *cnf,
                                    const char *name, const char *value)
{
    DSO *dso = NULL;
    conf_init_func *ifunc;
    conf_finish_func *ffunc;
    const char *path = NULL;
    int errcode = 0;
    CONF_MODULE *md;

    /* Look for alternative path in module section */
    path = _CONF_get_string(cnf, value, "path");
    if (path == NULL) {
        path = name;
    }
    dso = DSO_load(NULL, path, NULL, 0);
    if (dso == NULL) {
        errcode = CONF_R_ERROR_LOADING_DSO;
        goto err;
    }
    ifunc = (conf_init_func *)DSO_bind_func(dso, DSO_mod_init_name);
    if (ifunc == NULL) {
        errcode = CONF_R_MISSING_INIT_FUNCTION;
        goto err;
    }
    ffunc = (conf_finish_func *)DSO_bind_func(dso, DSO_mod_finish_name);
    /* All OK, add module */
    md = module_add(dso, name, ifunc, ffunc);

    if (md == NULL)
        goto err;

    return md;

 err:
    DSO_free(dso);
    ERR_raise_data(ERR_LIB_CONF, errcode, "module=%s, path=%s", name, path);
    return NULL;
}

4.dump_vmstate_json_to_file(vmstate_dump_file)

函数 dump_vmstate_json_to_file() 定义如下:

void dump_vmstate_json_to_file(FILE *out_file)
{
    GSList *list, *elt;
    bool first;

    fprintf(out_file, "{\n");
    dump_machine_type(out_file);

    first = true;
    list = object_class_get_list(TYPE_DEVICE, true);
    for (elt = list; elt; elt = elt->next) {
        DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
                                             TYPE_DEVICE);
        const char *name;
        int indent = 2;

        if (!dc->vmsd) {
            continue;
        }

        if (!first) {
            fprintf(out_file, ",\n");
        }
        name = object_class_get_name(OBJECT_CLASS(dc));
        fprintf(out_file, "%*s\"%s\": {\n", indent, "", name);
        indent += 2;
        fprintf(out_file, "%*s\"Name\": \"%s\",\n", indent, "", name);
        fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
                dc->vmsd->version_id);
        fprintf(out_file, "%*s\"minimum_version_id\": %d,\n", indent, "",
                dc->vmsd->minimum_version_id);

        dump_vmstate_vmsd(out_file, dc->vmsd, indent, false);

        fprintf(out_file, "\n%*s}", indent - 2, "");
        first = false;
    }
    fprintf(out_file, "\n}\n");
    fclose(out_file);
    g_slist_free(list);
}

函数 dump_vmstate_vmsd() 定义如下:

static void dump_vmstate_vmsd(FILE *out_file,
                              const VMStateDescription *vmsd, int indent,
                              bool is_subsection)
{
    if (is_subsection) {
        fprintf(out_file, "%*s{\n", indent, "");
    } else {
        fprintf(out_file, "%*s\"%s\": {\n", indent, "", "Description");
    }
    indent += 2;
    fprintf(out_file, "%*s\"name\": \"%s\",\n", indent, "", vmsd->name);
    fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
            vmsd->version_id);
    fprintf(out_file, "%*s\"minimum_version_id\": %d", indent, "",
            vmsd->minimum_version_id);
    if (vmsd->fields != NULL) {
        const VMStateField *field = vmsd->fields;
        bool first;

        fprintf(out_file, ",\n%*s\"Fields\": [\n", indent, "");
        first = true;
        while (field->name != NULL) {
            if (field->flags & VMS_MUST_EXIST) {
                /* Ignore VMSTATE_VALIDATE bits; these don't get migrated */
                field++;
                continue;
            }
            if (!first) {
                fprintf(out_file, ",\n");
            }
            dump_vmstate_vmsf(out_file, field, indent + 2);
            field++;
            first = false;
        }
        assert(field->flags == VMS_END);
        fprintf(out_file, "\n%*s]", indent, "");
    }
    if (vmsd->subsections != NULL) {
        const VMStateDescription **subsection = vmsd->subsections;
        bool first;

        fprintf(out_file, ",\n%*s\"Subsections\": [\n", indent, "");
        first = true;
        while (*subsection != NULL) {
            if (!first) {
                fprintf(out_file, ",\n");
            }
            dump_vmstate_vmss(out_file, subsection, indent + 2);
            subsection++;
            first = false;
        }
        fprintf(out_file, "\n%*s]", indent, "");
    }
    fprintf(out_file, "\n%*s}", indent - 2, "");
}

函数 dump_vmstate_vmsf() 定义如下:

static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field,
                              int indent)
{
    fprintf(out_file, "%*s{\n", indent, "");
    indent += 2;
    fprintf(out_file, "%*s\"field\": \"%s\",\n", indent, "", field->name);
    fprintf(out_file, "%*s\"version_id\": %d,\n", indent, "",
            field->version_id);
    fprintf(out_file, "%*s\"field_exists\": %s,\n", indent, "",
            field->field_exists ? "true" : "false");
    if (field->flags & VMS_ARRAY) {
        fprintf(out_file, "%*s\"num\": %d,\n", indent, "", field->num);
    }
    fprintf(out_file, "%*s\"size\": %zu", indent, "", field->size);
    if (field->vmsd != NULL) {
        fprintf(out_file, ",\n");
        dump_vmstate_vmsd(out_file, field->vmsd, indent, false);
    }
    fprintf(out_file, "\n%*s}", indent - 2, "");
}

函数 dump_vmstate_vmss() 定义如下:

static void dump_vmstate_vmss(FILE *out_file,
                              const VMStateDescription **subsection,
                              int indent)
{
    if (*subsection != NULL) {
        dump_vmstate_vmsd(out_file, *subsection, indent, true);
    }
}

总结

以上分析了 QEMU 系统仿真在启动过程中,QEMU 系统仿真导出虚拟机状态参数到文件中的代码实现。

相关推荐

  1. QEMU系统分析启动)】

    2024-04-24 16:52:03       26 阅读
  2. QEMU系统分析启动一)】

    2024-04-24 16:52:03       36 阅读
  3. QEMU系统分析启动七)】

    2024-04-24 16:52:03       35 阅读
  4. QEMU系统分析启动八)】

    2024-04-24 16:52:03       34 阅读
  5. QEMU系统分析启动九)】

    2024-04-24 16:52:03       32 阅读
  6. QEMU系统分析启动(二)】

    2024-04-24 16:52:03       33 阅读
  7. QEMU系统分析实例(二)】

    2024-04-24 16:52:03       25 阅读
  8. QEMU系统分析启动(九)】

    2024-04-24 16:52:03       56 阅读
  9. QEMU系统分析启动(八)】

    2024-04-24 16:52:03       30 阅读
  10. QEMU系统分析实例)】

    2024-04-24 16:52:03       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-24 16:52:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-24 16:52:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-24 16:52:03       82 阅读
  4. Python语言-面向对象

    2024-04-24 16:52:03       91 阅读

热门阅读

  1. 39、Lua 中调用C函数(lua-5.2.3)

    2024-04-24 16:52:03       33 阅读
  2. 基于Hadoop的石油大数据平台设计

    2024-04-24 16:52:03       31 阅读
  3. css中backface-visibility使用

    2024-04-24 16:52:03       37 阅读
  4. docker 故障解决

    2024-04-24 16:52:03       39 阅读
  5. bash test.sh 2>&1 &是什么意思?

    2024-04-24 16:52:03       101 阅读
  6. 国产系统注意事项

    2024-04-24 16:52:03       24 阅读