【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;...
}

运行后,输出信息如下:



总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jackson-jr 对比 Jackson

关于Jackson-jr 对比 Jackson 的内容&#xff0c;有人在做了一张下面的图。 简单点来说就 Jackson-jr 是Jackson 的轻量级应用&#xff0c;因为我们在很多时候都用不到 Jackson 的很多复杂功能。 对很多应用来说&#xff0c;我们可能只需要使用简单的 JSON 读写即可。 如我们…

【Linux网络】网络文件共享

目录 一、存储类型 二、FTP文件传输协议 2.1 FTP工作原理 2.2 FTP用户类型 2.3 FTP软件使用 2.3.1 服务端软件vsftpd 2.3.2 客户端软件ftp 2.4 FTP的应用 2.4.1 修改端口号 2.4.2 匿名用户的权限 2.4.3 传输速率 三、NFS 3.1 工作原理 3.2 NFS软件介绍 3.3 NFS配…

企业级数据治理学习总结

1. 水在前面 “数据治理”绝对是吹过的牛里面最高大上的题目了&#xff0c;本来想直接以《企业级数据治理》为题来水的&#xff0c;码字前又跑去图书馆借了几本书&#xff0c;翻了几页才发现自己连半桶水都提不起&#xff0c;撑死只能在小屁孩跟前吹吹牛。 好吧&#xff0c;实在…

怎么把jpg图片变成gif?参考这个方法一键制作

Jpg图片如何变成gif图片&#xff1f;Jpg、gif都是最常用的图片格式&#xff0c;想要将这两种格式的图片互相转化的时候要怎么操作呢&#xff1f;想要将jpg图片变成gif方法很简单&#xff0c;只需要使用gif图片制作&#xff08;https://www.gif5.net/&#xff09;工具-GIF5工具网…

华为手机ip地址怎么切换

随着移动互联网的普及&#xff0c;IP地址成为了我们手机上网的重要标识。然而&#xff0c;在某些情况下&#xff0c;我们可能需要切换手机的IP地址&#xff0c;以更好地保护个人隐私、访问特定地区的内容或服务&#xff0c;或者出于其他网络需求。华为手机作为市场上的热门品牌…

开源相机管理库Aravis学习——pixel format编码规则

开源相机管理库Aravis学习——pixel format编码规则 前言前置知识PixelFormatBpp 编码规则源码分析分类标准 补充ARV_PIXEL_FORMAT_BIT_PER_PIXEL 参考文章 前言 在学习Aravis官方例程的时候&#xff0c;有这么一个函数&#xff1a;arv_camera_get_pixel_format&#xff0c;它…

Docker安装MySQL8数据表名大小写问题

项目使用了Flowable工作流&#xff0c;导入sql时候需要MySQL忽略大小写&#xff0c;使用MySQL5.7时可以通过 在my.cnf文件中添加lower_case_table_names1实现。 [mysqld] lower_case_table_names1使用MySQL8上述方法失效&#xff0c;可以通过创建Docker容器时添加参数--lower_…

微信小程序开发中的网络请求和数据获取:从入门到实践

微信小程序开发中的网络请求和数据获取&#xff1a;从入门到实践 在微信小程序开发中&#xff0c;网络请求和数据获取是构建动态、交互式应用的关键环节。本文将深入探讨微信小程序的网络请求机制&#xff0c;并提供丰富的代码示例&#xff0c;帮助读者从零开始掌握这一重要技…

《从Paxos到Zookeeper》读书笔记

本文是对于从Paxos到Zookeeper的笔记和提纲整理 主要用于复习和知识点梳理 第一、二、三章&#xff1a;分布式基础 链接&#xff1a;《从Paxos到Zookeeper》——第一、二、三章&#xff1a;分布式基础 第一章 分布式架构 [1.1] 分布式的特点&#xff08;集中式/分布式&#xf…

C语言经典例题-12

1.杨辉三角 题目描述: KK知道什么叫杨辉三角之后对杨辉三角产生了浓厚的兴趣&#xff0c;他想知道杨辉三角的前n行&#xff0c;请编程帮他 解答。杨辉三角&#xff0c;本质上是二项式(ab)的n次方展开后各项的系数排成的三角形。其性质包括&#xff1a;每行的端点数为1&#…

用队列实现栈——leetcode刷题

题目的要求是用两个队列实现栈&#xff0c;首先我们要考虑队列的特点&#xff1a;先入先出&#xff0c;栈的特点&#xff1a;后入先出&#xff0c;所以我们的目标就是如何让先入栈的成员后出栈&#xff0c;后入栈的成员先出栈。 因为有两个队列&#xff0c;于是我们可以这样想&…

vue3(实现上下无限来往滚动)

一、问题描述 一般在大屏项目中&#xff0c;很常见的效果&#xff0c;就是容器中的内容缓慢地向下移动&#xff0c;直到底部停止&#xff0c;然后快速滚动回顶部&#xff0c;然后接着缓慢滚动到底部。并且在特定的情况下&#xff0c;还需要进行一些小交互&#xff0c;那就还得让…

Leetcode——面试题02.04.分割链表

面试题 02.04. 分割链表 - 力扣&#xff08;LeetCode&#xff09; 对于该链表OJ&#xff0c;我们两种大的方向&#xff1a; 1.在原链表上修改&#xff1b;2.创建新链表&#xff0c;遍历原链表。 在原链上进行修改&#xff1a;如果该节点的val小于x则继续往后走&#xff0c;如…

2.3Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3-基础-Vue进阶

Vue方法、计算属性及监听器 在vue中处理复杂的逻辑的时候&#xff0c;我们经常使用计算属性、方法及监听器。 methods&#xff1a;方法&#xff1a;它们是挂载在Vue对象上的函数&#xff0c;通常用于做事件处理函数&#xff0c;或自己封装的自定义函数。 computed&#xff1…

Ubuntu服务器创建新用户及解决新用户登录Access denied问题

目录 Ubuntu服务器创建新用户及解决新用户登录Access denied问题创建账号步骤创建用户只创建用户添加用户到sudo组 允许账号远程连接重启ssh服务 删除账号要删除用户而不删除用户文件如果要删除并且删除用户的家目录和邮件 查询指令查看所有用户查询特定用户账户信息查看用户组…

Java中面向对象三大特征(封装、继承、多态)

目录 一、封装 1.1 封装的意义 1.2 如何进行封装 二、继承 2.1 继承的意义 2.2 如何继承 2.3 继承的优点 2.4 继承的缺点 三、多态 3.1 多态的定义 3.2 多态的使用要求 一、封装 所谓封装就是将对象的属性隐藏起来&#xff0c;不让外界直接访问&#xff0c;而是通过…

【Micropython Pitaya Lite教程】key按键与EXTI中断

文章目录 前言一、按键的使用1.1 按键的简介1.2 读取按键的高低电平 二、EXIT外部中断2.1 EXIT外部中断简介2.2 外部中断基础知识2.3 设置外部中断2.4 示例代码 总结 前言 Micropython Pitaya Lite开发板提供了丰富的功能和灵活的扩展性&#xff0c;其中包括了按键&#xff08…

Python 全栈系列241 GFGo Lite迭代

说明 随着整个算网开发逐渐深入&#xff0c;各个组件、微服务的数量、深度在不断增加。由于算网是个人项目&#xff0c;我一直按照MVP(Minimum Viable Product )的原则在推进。由于最初的时候对架构、算法和业务的理解并没有那么深刻&#xff0c;所以MVP的内容还是在不断变化&…

精准医疗中的图像识别技术:从诊断到治疗的新变革

在精准医疗中&#xff0c;图像识别技术的应用正在从诊断到治疗带来一场新的变革。这种技术通过深度学习和计算机视觉算法&#xff0c;能够分析和解释医学图像&#xff0c;如X射线、CT扫描、MRI等&#xff0c;从而帮助医生更准确地诊断疾病&#xff0c;制定更个性化的治疗方案。…

IoTDB 入门教程 基础篇①——时序数据库为什么选IoTDB ?

文章目录 一、前文二、性能排行第一三、完全开源四、数据文件TsFile五、乱序数据高写入六、其他七、参考 一、前文 IoTDB入门教程——导读 关注博主的同学都知道&#xff0c;博主在物联网领域深耕多年。 时序数据库&#xff0c;博主已经用过很多&#xff0c;从最早的InfluxDB&a…