【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

相关文章

Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案

《Vue3组件中getCurrentInstance()获取App实例,但是返回null的解决方案》:本文主要介绍Vue3组件中getCurrentInstance()获取App实例,但是返回nu... 目录vue3组件中getCurrentInstajavascriptnce()获取App实例,但是返回n

SQL表间关联查询实例详解

《SQL表间关联查询实例详解》本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(leftjoin)、右连接(rightjoin)、全连接(fulljoin)、内连接(innerjoin)、... 目录简介样例准备左外连接右外连接全外连接内连接交叉连接自然连接简介本文主要讲解SQL语句中常用的表

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

springboot security验证码的登录实例

《springbootsecurity验证码的登录实例》:本文主要介绍springbootsecurity验证码的登录实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录前言代码示例引入依赖定义验证码生成器定义获取验证码及认证接口测试获取验证码登录总结前言在spring

tomcat多实例部署的项目实践

《tomcat多实例部署的项目实践》Tomcat多实例是指在一台设备上运行多个Tomcat服务,这些Tomcat相互独立,本文主要介绍了tomcat多实例部署的项目实践,具有一定的参考价值,感兴趣的可... 目录1.创建项目目录,测试文China编程件2js.创建实例的安装目录3.准备实例的配置文件4.编辑实例的

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景

MyBatis-Plus中静态工具Db的多种用法及实例分析

《MyBatis-Plus中静态工具Db的多种用法及实例分析》本文将详细讲解MyBatis-Plus中静态工具Db的各种用法,并结合具体案例进行演示和说明,具有很好的参考价值,希望对大家有所帮助,如有... 目录MyBATis-Plus中静态工具Db的多种用法及实例案例背景使用静态工具Db进行数据库操作插入

Spring中@Lazy注解的使用技巧与实例解析

《Spring中@Lazy注解的使用技巧与实例解析》@Lazy注解在Spring框架中用于延迟Bean的初始化,优化应用启动性能,它不仅适用于@Bean和@Component,还可以用于注入点,通过将... 目录一、@Lazy注解的作用(一)延迟Bean的初始化(二)与@Autowired结合使用二、实例解