【QEMU系统分析之实例篇(十四)】

2024-05-05 08:44

本文主要是介绍【QEMU系统分析之实例篇(十四)】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

第十四章 QEMU系统仿真的机器创建分析实例


文章目录

  • 系列文章目录
    • 第十四章 QEMU系统仿真的机器创建分析实例
  • 前言
  • 一、QEMU是什么?
  • 二、QEMU系统仿真的机器创建分析实例
    • 1.系统仿真的命令行参数
    • 2.完成早期后端驱动的设置工作
      • qemu_create_early_backends()
        • configure_blockdev();
        • audio_init_audiodevs()
        • audio_create_default_audiodevs()
    • 3.调试输出
  • 总结


前言

本文以 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 系统启动过程的程序代码有所了解,相关内容可以参考《QEMU系统分析之启动篇》系列文章。

..\qemu\8.2.2-qkd\qemu-system-x86_64.exe -cpu "Penryn" -M  "q35,accel=whpx,smm=off" -m "6G" -display "sdl" -audio "sdl,model=hda" -vga "std" -L "data"

2.完成早期后端驱动的设置工作

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

int qemu_init(int argc, char **argv)
{
...qemu_create_early_backends();
...
}

前文分析了创建后端驱动过程中控制台和字符设备的创建过程,本文继续完成块设备和音频设备驱动的创建过程。


qemu_create_early_backends()

函数 qemu_create_early_backends() 代码如下:

static void qemu_create_early_backends(void)
{
.../** Note: we need to create audio and block backends before* setting machine properties, so they can be referred to.*/configure_blockdev(&bdo_queue, machine_class, snapshot);audio_init_audiodevs();if (default_audio) {audio_create_default_audiodevs();}
}

首先我们对块设备后端驱动进行配置。


configure_blockdev();

代码如下:

static void configure_blockdev(BlockdevOptionsQueue *bdo_queue,MachineClass *machine_class, int snapshot)
{/** If the currently selected machine wishes to override the* units-per-bus property of its default HBA interface type, do so* now.*/if (machine_class->units_per_default_bus) {override_max_devs(machine_class->block_default_type,machine_class->units_per_default_bus);}/* open the virtual block devices */while (!QSIMPLEQ_EMPTY(bdo_queue)) {BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(bdo_queue);QSIMPLEQ_REMOVE_HEAD(bdo_queue, entry);loc_push_restore(&bdo->loc);qmp_blockdev_add(bdo->bdo, &error_fatal);loc_pop(&bdo->loc);qapi_free_BlockdevOptions(bdo->bdo);g_free(bdo);}if (snapshot) {qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot,NULL, NULL);}if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,&machine_class->block_default_type, &error_fatal)) {/* We printed help */exit(0);}default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2,CDROM_OPTS);default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);}

audio_init_audiodevs()

代码如下:

void audio_init_audiodevs(void)
{AudiodevListEntry *e;QSIMPLEQ_FOREACH(e, &audiodevs, next) {audio_init(e->dev, &error_fatal);}
}

对 audiodevs 中的每个音频设备调用函数 audio_init() 完成初始化。

函数 audio_init() 代码如下:

/** if we have dev, this function was called because of an -audiodev argument =>*   initialize a new state with it* if dev == NULL => legacy implicit initialization, return the already created*   state or create a new one*/
static AudioState *audio_init(Audiodev *dev, Error **errp)
{static bool atexit_registered;int done = 0;const char *drvname;VMChangeStateEntry *vmse;AudioState *s;struct audio_driver *driver;s = g_new0(AudioState, 1);QLIST_INIT (&s->hw_head_out);QLIST_INIT (&s->hw_head_in);QLIST_INIT (&s->cap_head);if (!atexit_registered) {atexit(audio_cleanup);atexit_registered = true;}s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);if (dev) {/* -audiodev option */s->dev = dev;drvname = AudiodevDriver_str(dev->driver);driver = audio_driver_lookup(drvname);if (driver) {done = !audio_driver_init(s, driver, dev, errp);} else {error_setg(errp, "Unknown audio driver `%s'", drvname);}if (!done) {goto out;}} else {assert(!default_audio_state);for (;;) {AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);if (!e) {error_setg(errp, "no default audio driver available");goto out;}s->dev = dev = e->dev;QSIMPLEQ_REMOVE_HEAD(&default_audiodevs, next);g_free(e);drvname = AudiodevDriver_str(dev->driver);driver = audio_driver_lookup(drvname);if (!audio_driver_init(s, driver, dev, NULL)) {break;}qapi_free_Audiodev(dev);s->dev = NULL;}}if (dev->timer_period <= 0) {s->period_ticks = 1;} else {s->period_ticks = dev->timer_period * (int64_t)SCALE_US;}vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);if (!vmse) {dolog ("warning: Could not register change state handler\n""(Audio can continue looping even after stopping the VM)\n");}QTAILQ_INSERT_TAIL(&audio_states, s, list);QLIST_INIT (&s->card_head);vmstate_register_any(NULL, &vmstate_audio, s);return s;out:free_audio_state(s);return NULL;
}

audio_create_default_audiodevs()

最后,如果设置了 default_audio,调用函数 audio_create_default_audiodevs() 创建默认音频设备,代码如下:

void audio_create_default_audiodevs(void)
{for (int i = 0; audio_prio_list[i]; i++) {if (audio_driver_lookup(audio_prio_list[i])) {QDict *dict = qdict_new();Audiodev *dev = NULL;Visitor *v;qdict_put_str(dict, "driver", audio_prio_list[i]);qdict_put_str(dict, "id", "#default");v = qobject_input_visitor_new_keyval(QOBJECT(dict));qobject_unref(dict);visit_type_Audiodev(v, NULL, &dev, &error_fatal);visit_free(v);audio_define_default(dev, &error_abort);}}
}

3.调试输出

首先,添加跟踪调试信息,修改后的代码如下:

```c
static void qemu_create_early_backends(void)
{...huedbg_flag = 1;HUEDBG("\n");huedbg_dump_device_configs(2);HUEDBG("\n");qemu_create_early_backends();HUEDBG("\n");huedbg_dump_device_configs(2);HUEDBG("\n");huedbg_flag = 0;...
}

运行后,输出信息如下:



总结

以上分析了系统初始化过程中创建早期后端驱动的过程。

这篇关于【QEMU系统分析之实例篇(十四)】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/961280

相关文章

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

mysqld_multi在Linux服务器上运行多个MySQL实例

《mysqld_multi在Linux服务器上运行多个MySQL实例》在Linux系统上使用mysqld_multi来启动和管理多个MySQL实例是一种常见的做法,这种方式允许你在同一台机器上运行多个... 目录1. 安装mysql2. 配置文件示例配置文件3. 创建数据目录4. 启动和管理实例启动所有实例

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss