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

系列文章目录

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


文章目录

  • 系列文章目录
    • 第十五章 QEMU系统仿真的导出虚拟机分析
  • 前言
  • 一、QEMU是什么?
  • 二、QEMU系统仿真的启动分析
    • 1.系统仿真的初始化代码
    • 2.主循环数据初始化
    • 3. module_load_qom_all()
      • module_check_arch()
      • module_load()
    • 4.dump_vmstate_json_to_file(vmstate_dump_file)
  • 总结


前言

本文以 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 系统仿真导出虚拟机状态参数到文件中的代码实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/571274.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

商汤大模型一体机可节约80%推理成本,完成云端边全栈布局

商汤科技在上海举办技术交流日活动 会上商汤发布业内首个“云、端、边”全栈大模型产品矩阵&#xff0c;并推出全新升级的“日日新SenseNova 5.0”大模型体系。 影响至股价方面&#xff0c;商汤(00020.HK)今日开盘后大幅上涨。随后商汤在港交所公告称&#xff0c;商汤集团的…

《看漫画学C++》背后的故事1:艺术与科技的结合

引言&#xff1a; 在数字化浪潮中&#xff0c;艺术与科技的结合催生了无数创新。《看漫画学C》正是这一跨界合作的产物&#xff0c;它不仅是一本编程书籍&#xff0c;更是艺术与科技融合的典范。 一、相遇&#xff1a; 科技与艺术的火花作为一名专注于技术的软件程序员&…

【JAVA基础之IO】字节流、字符流以及乱码问题

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1. IO概述 1.1 什么是IO 1.2 IO的分类 1.3 字节和字符流的顶级父类 2. 字节流 2.1 一切皆为字节 2.2 字节输出流【OutputStream】 2.3 FileOutputStream类…

mfc140.dll丢失如何修复,分享多种有效的修复方法

在日常操作和使用电脑的过程中&#xff0c;我们可能会遇到一种较为常见的问题&#xff0c;即在尝试启动或运行某个应用程序时&#xff0c;系统突然弹出一个错误提示窗口&#xff0c;明确指出“mfc140.dll文件丢失”。这个mfc140.dll实际上是一个动态链接库文件&#xff08;DLL&…

Flask框架初探-如何在本机发布一个web服务并通过requests访问自己发布的服务-简易入门版

Flask框架初探 在接触到网络框架之前我其实一直对一个事情有疑惑&#xff0c;跨语言的API在需要传参的情况下究竟应该如何调用&#xff0c;之前做过的项目里&#xff0c;我用python做了一个代码使用一个算法得到一个结果之后我应该怎么给到做前端的同学或者同事&#xff0c;之前…

微信小程序使用echarts组件实现饼状统计图功能

微信小程序使用echarts组件实现饼状统计图功能 使用echarts实现在微信小程序中统计图的功能&#xff0c;具体的实现步骤思路可进我主页查看我的另一篇博文https://blog.csdn.net/weixin_45465881/article/details/138171153进行查看&#xff0c;本篇文章主要使用echarts组件实…

移动桌面均覆盖-免费使用,解锁VIP!

hello&#xff0c;我是小索奇 分享一下剪映&#xff0c;附带windows和移动端哈 软件内所有功能都已经解锁了哈&#xff01;不登录也是会员特权~ 相对于PR来说&#xff0c;用剪映省去了好多痛苦&#xff08;用过的都懂&#xff0c;都是包装&#xff09; 软件下载好之后双…

【计算机考研】计算机行业考研还有性价比吗?

刚上大学的时候觉得本科毕业就够了 到了大三找工作才发现自己什么都不会 只能踏上考研路 目前研一在读&#xff0c;身边有很多工作了几年又回来读研的同学&#xff0c;只能说现在计算机行业研究生还是比本科生有竞争力的 如果你的本科和我一样没有学什么技术&#xff0c;那…

实现Node.js安装与配置。

一 、Node.js简介 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;用于构建高性能、可扩展的网络应用程序。它发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;允许使用JavaScript进行服务器端编程&#xff0c;使开发者能够在前后端都使用同一种编程…

MessageBox的模版语法上如何注册事件

目标&#xff1a; 想要在MessageBox中的文本中写一些事件&#xff0c;如何处理 在vue中进行操作&#xff1a; coding // 弹窗提示 this.$confirm({customClass: "add-fail",title: this.$t(add_error),type: error,dangerouslyUseHTMLString: true, // 将消息使用…

【学习笔记二十六】EWM 盘点后台配置和前台演示

一、EWM盘点业务概述 SAP EWM(Extended Warehouse Management)模块中的盘点业务是一个关键流程,它允许企业对仓库中的库存进行实物清点,以确保系统中记录的数据与实际库存相符。盘点过程有助于企业识别库存差异,从而进行必要的库存调整,确保库存记录的准确性,这对于财务…

电商技术揭秘三十二:智能风控的案例研究与未来趋势

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

Excel 冻结前几行

Excel中有冻结首航和冻结首列的选项&#xff0c;但是如果想冻结前几行该怎么操作&#xff1f; 冻结首行或冻结首列 视图 -> 冻结窗格 -> 冻结首行或冻结首列 冻结前几行或前几列 视图 -> 冻结窗格 -> 冻结拆分窗格 具体冻结几行和几列取决于当前选中的单元格。…

leetcode 每日一题目 (树的直径 +DFS的深刻理解)

如下是题目的简单描述&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟&#xff0c;感染 将会从值为 start 的节点开始爆发。 每分钟&#xff0c;如果节点满足以下全部条件&#xff0c;就会被感染&…

2. 多机多卡运行nccl-tests对比分析

系列文章 第2章 多机多卡nccl-tests 对比分析 目录 系列文章前言一、本地环境1. 网卡接口2. RDMA3. TOPO信息pcie信息nvidia-smi topo -m 二、nccl-test对比分析1. 相关环境变量2. 不同情况的对比3. 总结与分析 前言 NCCL&#xff08;NVIDIA Collective Communications Libra…

带头双向循环链表的基本操作(c语言实现)

带头双向循环链表 带头双向循环链表是一种结合了双向链表和循环链表特性的数据结构。其主要特点如下&#xff1a; 双向性&#xff1a;链表中的每个节点都有两个指针&#xff0c;一个指向下一个节点&#xff08;next&#xff09;&#xff0c;另一个指向前一个节点&#xff08;p…

11.泛型

文章目录 1 泛型概念2. 自定义泛型结构3 泛型方法4 泛型在继承上的体现5 通配符的使用 1 泛型概念 所谓泛型就是用标识符标识不确定的类型&#xff0c;详细说就是&#xff1a;定义类或接口时用标识符表示类中某个属性的类型或者是某个方法的返回值及参数类型。泛型将在使用时&a…

《QT实用小工具·三十九》仿 Windows10 画图3D 的颜色选择器, 但更加强大

1、概述 源码放在文章末尾 该项目实现了仿 Windows10 画图3D 的颜色选择器&#xff0c;功能更加丰富更加强大。 项目部分代码如下所示&#xff1a; import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtGraphicalEffects 1.15Item {id…

基于OSAL 实现UART、LED、ADC等基础示例 4

1 UART 实验目的 串口在我们开发单片机项目是很重要的&#xff0c;可以观察我们的代码运行情况&#xff0c;本节的目的就 是实现串口双工收发。 虽然说 osal 相关的代码已经跟硬件关系不大了&#xff0c;但是我们还是来贴出相关的硬件原理图贴出来。 1.1 初始化 osal_init_s…

Leetcode743. 网络延迟时间

Every day a Leetcode 题目来源&#xff1a;743. 网络延迟时间 本题需要用到单源最短路径算法 Dijkstra&#xff0c;现在让我们回顾该算法&#xff0c;其主要思想是贪心。 将所有节点分成两类&#xff1a;已确定从起点到当前点的最短路长度的节点&#xff0c;以及未确定从起…