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

embedded/2024/9/20 3:55:25/ 标签: 网络, harmonyos, windows, 嵌入式硬件, macos

系列文章目录

第十七章 QEMU系统仿真的显示初始化分析


文章目录

  • 系列文章目录
    • 第十七章 QEMU系统仿真的显示初始化分析
  • 前言
  • 一、QEMU是什么?
  • 二、QEMU系统仿真的启动分析
    • 1.系统仿真的初始化代码
    • 2.主循环数据初始化
    • 3. qemu_init_displays()
      • init_displaystate()
      • qemu_display_init()
      • os_setup_signal_handling()
      • qemu_spice.display_init()
        • qemu_console_lookup_by_device_name()
          • qemu_console_lookup_by_device()
        • qemu_console_lookup_by_index()
        • qemu_console_is_graphic()
        • qemu_spice_have_display_interface()
        • qemu_spice_display_init_one()
          • qemu_spice_display_init_common()
          • qemu_bh_new()
          • timer_new_ms()
          • qemu_gl_init_shader()
          • qemu_spice_add_display_interface()
          • qemu_console_fill_device_address()
          • spice_qxl_set_device_info()
          • qemu_console_get_head()
          • qemu_spice_create_host_memslot()
          • qemu_console_set_display_gl_ctx()
          • register_displaychangelistener()
        • qemu_spice_display_init_done()
          • runstate_is_running()
          • qemu_spice_display_start()
          • qemu_add_vm_change_state_handler()
          • vm_change_state_handler()
  • 总结


前言

本文以 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 虚拟机导出信息的设置,接下来将处理预配置的工作,本篇文章将完成以下代码部分的分析。

2.主循环数据初始化

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

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

3. qemu_init_displays()

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

static void qemu_init_displays(void)
{DisplayState *ds;/* init local displays */ds = init_displaystate();qemu_display_init(ds, &dpy);/* must be after terminal init, SDL library changes signal handlers */os_setup_signal_handling();/* init remote displays */
#ifdef CONFIG_VNCqemu_opts_foreach(qemu_find_opts("vnc"),vnc_init_func, NULL, &error_fatal);
#endifif (using_spice) {qemu_spice.display_init();}
}

init_displaystate()

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

static DisplayState *display_state;/** Called by main(), after creating QemuConsoles* and before initializing ui (sdl/vnc/...).*/
DisplayState *init_displaystate(void)
{gchar *name;QemuConsole *con;QTAILQ_FOREACH(con, &consoles, next) {/* Hook up into the qom tree here (not in object_new()), once* all QemuConsoles are created and the order / numbering* doesn't change any more */name = g_strdup_printf("console[%d]", con->index);object_property_add_child(container_get(object_get_root(), "/backend"),name, OBJECT(con));g_free(name);}return display_state;
}

数据结构 DisplayState 定义如下:

struct DisplayState {QEMUTimer *gui_timer;uint64_t last_update;uint64_t update_interval;bool refreshing;QLIST_HEAD(, DisplayChangeListener) listeners;
};

qemu_display_init()

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

void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
{assert(opts->type < DISPLAY_TYPE__MAX);if (opts->type == DISPLAY_TYPE_NONE) {return;}assert(dpys[opts->type] != NULL);dpys[opts->type]->init(ds, opts);
}

其中,DISPLAY_TYPE__MAX 定义如下:

typedef enum DisplayType {DISPLAY_TYPE_DEFAULT,DISPLAY_TYPE_NONE,
#if defined(CONFIG_GTK)DISPLAY_TYPE_GTK,
#endif /* defined(CONFIG_GTK) */
#if defined(CONFIG_SDL)DISPLAY_TYPE_SDL,
#endif /* defined(CONFIG_SDL) */
#if defined(CONFIG_OPENGL)DISPLAY_TYPE_EGL_HEADLESS,
#endif /* defined(CONFIG_OPENGL) */
#if defined(CONFIG_CURSES)DISPLAY_TYPE_CURSES,
#endif /* defined(CONFIG_CURSES) */
#if defined(CONFIG_COCOA)DISPLAY_TYPE_COCOA,
#endif /* defined(CONFIG_COCOA) */
#if defined(CONFIG_SPICE)DISPLAY_TYPE_SPICE_APP,
#endif /* defined(CONFIG_SPICE) */
#if defined(CONFIG_DBUS_DISPLAY)DISPLAY_TYPE_DBUS,
#endif /* defined(CONFIG_DBUS_DISPLAY) */DISPLAY_TYPE__MAX,
} DisplayType;

os_setup_signal_handling()

函数 os_setup_signal_handling() 在 Windows 系统中的定义如下:

static inline void os_setup_signal_handling(void) {}

在 POSIX 系统中定义如下:

void os_setup_signal_handling(void)
{struct sigaction act;memset(&act, 0, sizeof(act));act.sa_sigaction = termsig_handler;act.sa_flags = SA_SIGINFO;sigaction(SIGINT,  &act, NULL);sigaction(SIGHUP,  &act, NULL);sigaction(SIGTERM, &act, NULL);
}

qemu_spice.display_init()

变量 qemu_spice 定义如下:

struct QemuSpiceOps qemu_spice = {.init         = qemu_spice_init_stub,.display_init = qemu_spice_display_init_stub,.migrate_info = qemu_spice_migrate_info_stub,.set_passwd   = qemu_spice_set_passwd_stub,.set_pw_expire = qemu_spice_set_pw_expire_stub,.display_add_client = qemu_spice_display_add_client_stub,
};

因此,函数 qemu_spice.display_init() 实际调用 qemu_spice_display_init_stub(),如果定义了 CONFIG_SPICE,则调用 /ui/spice-display.c 文件中的函数 qemu_spice_display_init(),定义如下:

void qemu_spice_display_init(void)
{QemuOptsList *olist = qemu_find_opts("spice");QemuOpts *opts = QTAILQ_FIRST(&olist->head);QemuConsole *spice_con, *con;const char *str;int i;str = qemu_opt_get(opts, "display");if (str) {int head = qemu_opt_get_number(opts, "head", 0);Error *err = NULL;spice_con = qemu_console_lookup_by_device_name(str, head, &err);if (err) {error_report("Failed to lookup display/head");exit(1);}} else {spice_con = NULL;}for (i = 0;; i++) {con = qemu_console_lookup_by_index(i);if (!con || !qemu_console_is_graphic(con)) {break;}if (qemu_spice_have_display_interface(con)) {continue;}if (spice_con != NULL && spice_con != con) {continue;}qemu_spice_display_init_one(con);}qemu_spice_display_init_done();
}

qemu_console_lookup_by_device_name()

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

QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,uint32_t head, Error **errp)
{DeviceState *dev;QemuConsole *con;dev = qdev_find_recursive(sysbus_get_default(), device_id);if (dev == NULL) {error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,"Device '%s' not found", device_id);return NULL;}con = qemu_console_lookup_by_device(dev, head);if (con == NULL) {error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",device_id, head);return NULL;}return con;
}
qemu_console_lookup_by_device()

函数 qemu_console_lookup_by_device() 定义如下:

QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
{QemuConsole *con;Object *obj;uint32_t h;QTAILQ_FOREACH(con, &consoles, next) {obj = object_property_get_link(OBJECT(con),"device", &error_abort);if (DEVICE(obj) != dev) {continue;}h = object_property_get_uint(OBJECT(con),"head", &error_abort);if (h != head) {continue;}return con;}return NULL;
}

qemu_console_lookup_by_index()

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

QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{QemuConsole *con;QTAILQ_FOREACH(con, &consoles, next) {if (con->index == index) {return con;}}return NULL;
}

qemu_console_is_graphic()

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

bool qemu_console_is_graphic(QemuConsole *con)
{if (con == NULL) {con = active_console;}return con && QEMU_IS_GRAPHIC_CONSOLE(con);
}

qemu_spice_have_display_interface()

此函数在 /ui/spice-core.c 文件中,定义如下:

bool qemu_spice_have_display_interface(QemuConsole *con)
{if (g_slist_find(spice_consoles, con)) {return true;}return false;
}

qemu_spice_display_init_one()

此函数在 /ui/spice-display.c 文件中,定义如下:

static void qemu_spice_display_init_one(QemuConsole *con)
{SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);qemu_spice_display_init_common(ssd);ssd->dcl.ops = &display_listener_ops;
#ifdef HAVE_SPICE_GLif (spice_opengl) {ssd->dcl.ops = &display_listener_gl_ops;ssd->dgc.ops = &gl_ctx_ops;ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,qemu_spice_gl_block_timer, ssd);ssd->gls = qemu_gl_init_shader();ssd->have_surface = false;ssd->have_scanout = false;}
#endifssd->dcl.con = con;ssd->qxl.base.sif = &dpy_interface.base;qemu_spice_add_display_interface(&ssd->qxl, con);#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */Error *err = NULL;char device_address[256] = "";if (qemu_console_fill_device_address(con, device_address, 256, &err)) {spice_qxl_set_device_info(&ssd->qxl,device_address,qemu_console_get_head(con),1);} else {error_report_err(err);}
#endifqemu_spice_create_host_memslot(ssd);if (spice_opengl) {qemu_console_set_display_gl_ctx(con, &ssd->dgc);}register_displaychangelistener(&ssd->dcl);
}
qemu_spice_display_init_common()
qemu_bh_new()
timer_new_ms()
qemu_gl_init_shader()
qemu_spice_add_display_interface()
qemu_console_fill_device_address()
spice_qxl_set_device_info()
qemu_console_get_head()
qemu_spice_create_host_memslot()
qemu_console_set_display_gl_ctx()
register_displaychangelistener()

qemu_spice_display_init_done()

此函数在 /ui/spice-display.c 文件中,定义如下:

void qemu_spice_display_init_done(void)
{if (runstate_is_running()) {qemu_spice_display_start();}qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
}
runstate_is_running()

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

bool runstate_is_running(void)
{return runstate_check(RUN_STATE_RUNNING);
}bool runstate_check(RunState state)
{return current_run_state == state;
}
qemu_spice_display_start()

函数 qemu_spice_display_start() 定义如下:

void  qemu_spice_display_start(void)
{if (spice_display_is_running) {return;}spice_display_is_running = true;spice_server_vm_start(spice_server);
}
qemu_add_vm_change_state_handler()

此函数在 /ui/spice-core.c 文件中,定义如下:

/*** qemu_add_vm_change_state_handler_prio:* @cb: the callback to invoke* @opaque: user data passed to the callback* @priority: low priorities execute first when the vm runs and the reverse is*            true when the vm stops** Register a callback function that is invoked when the vm starts or stops* running.** Returns: an entry to be freed using qemu_del_vm_change_state_handler()*/
VMChangeStateEntry *qemu_add_vm_change_state_handler_prio(VMChangeStateHandler *cb, void *opaque, int priority)
{return qemu_add_vm_change_state_handler_prio_full(cb, NULL, opaque,priority);
}/*** qemu_add_vm_change_state_handler_prio_full:* @cb: the main callback to invoke* @prepare_cb: a callback to invoke before the main callback* @opaque: user data passed to the callbacks* @priority: low priorities execute first when the vm runs and the reverse is*            true when the vm stops** Register a main callback function and an optional prepare callback function* that are invoked when the vm starts or stops running. The main callback and* the prepare callback are called in two separate phases: First all prepare* callbacks are called and only then all main callbacks are called. As its* name suggests, the prepare callback can be used to do some preparatory work* before invoking the main callback.** Returns: an entry to be freed using qemu_del_vm_change_state_handler()*/
VMChangeStateEntry *
qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb,VMChangeStateHandler *prepare_cb,void *opaque, int priority)
{VMChangeStateEntry *e;VMChangeStateEntry *other;e = g_malloc0(sizeof(*e));e->cb = cb;e->prepare_cb = prepare_cb;e->opaque = opaque;e->priority = priority;/* Keep list sorted in ascending priority order */QTAILQ_FOREACH(other, &vm_change_state_head, entries) {if (priority < other->priority) {QTAILQ_INSERT_BEFORE(other, e, entries);return e;}}QTAILQ_INSERT_TAIL(&vm_change_state_head, e, entries);return e;
}VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,void *opaque)
{return qemu_add_vm_change_state_handler_prio(cb, opaque, 0);
}
vm_change_state_handler()

此函数在 /ui/spice-core.c 文件中,定义如下:

static void vm_change_state_handler(void *opaque, bool running,RunState state)
{if (running) {qemu_spice_display_start();} else if (state != RUN_STATE_PAUSED) {qemu_spice_display_stop();}
}

函数 qemu_spice_display_start() 和 qemu_spice_display_stop() 定义如下:

void  qemu_spice_display_start(void)
{if (spice_display_is_running) {return;}spice_display_is_running = true;spice_server_vm_start(spice_server);
}void qemu_spice_display_stop(void)
{if (!spice_display_is_running) {return;}spice_server_vm_stop(spice_server);spice_display_is_running = false;
}

至此,函数 qemu_spice.display_init() 执行完毕,同时主程序的函数 qemu_init_displays() 也执行完毕。


总结

以上分析了 QEMU 系统仿真在启动过程中,QEMU系统仿真完成显示初始化的代码。


http://www.ppmy.cn/embedded/16476.html

相关文章

区块链技术与应用学习笔记(8-9节)——北大肖臻课程

目录 8.挖矿 对于全节点和轻节点思考问题&#xff1f; ①全节点在比特币的主要作用&#xff1f; ②挖矿时当监听到别人已经挖出区块并且延申了最长合法链此时应该立刻放弃当前区块在 本地重新组装一个指向最后这个新合法区块的候选区块&#xff0c;重新开始挖矿。节点这么做…

Swift - Hello World

文章目录 Swift - Hello World1. Hello World Swift - Hello World 1. Hello World 不用编写main函数&#xff0c;Swift将全局范围内的首句可执行代码作为程序入口一句代码尾部可以省略分号&#xff08;;&#xff09;&#xff0c;多句代码写到同一行时必须用分号&#xff08;…

蚓链数字化营销系统与数字资产的关系

蚓链数字化营销系统是一种利用数字技术来实现营销目标的系统。它集成了多种数字营销工具和渠道&#xff0c;以收集、分析和利用客户数据&#xff0c;优化营销活动&#xff0c;并提高营销效果。 数字资产是一种新型的资产类别&#xff0c;它们以电子数据的形式存在&#xff0c;可…

基于Python实现心脏病数据可视化DEA+预测【500010103.1】

一、数据说明 该心脏病数据集是通过组合 5 个已经独立可用但以前未合并的流行心脏病数据集来策划的。在这个数据集中&#xff0c;5 个心脏数据集结合了 11 个共同特征&#xff0c;使其成为迄今为止可用于研究目的的最大心脏病数据集。 该数据集由 1190 个实例和 11 个特征组成…

Git工具的使用

文章目录 Git概述本地仓库命令远程仓库命令分支操作标签操作 IDEA上执行Git Git概述 一般工作流程如下&#xff1a; 从远程仓库中克隆 Git 资源作为本地仓库&#xff1b; 从本地仓库中checkout代码然后进行代码修改&#xff1b; 在提交本地仓库前先将代码提交到暂存区&#xff…

Qt——自定义富文本RichText

作者&#xff1a;小 琛 欢迎转载&#xff0c;请标明出处 文章目录 产品中的富文本富文本控件会面临的问题QTextBrowser例子&#xff1a;自定义富文本 产品中的富文本 用户界面中支持显示富文本格式&#xff08;如加粗、斜体、不同颜色、超链接等&#xff09;的文本内容。这种富…

政安晨:【Keras机器学习示例演绎】(十六)—— 用于图像分类的混合增强

目录 简介 设置 准备数据集 定义超参数 将数据转换为 TensorFlow 数据集对象 定义混合技术函数 可视化新的增强数据集 模型制作 1.使用混合数据集训练模型 2.在没有混合数据集的情况下训练模型 说明 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评…

C#鼠标拖拽无边框浮动窗体的方法:窗体控制

目录 (1)ReleaseCapture函数 (2)SendMessage函数 (3)实例 1.Resources.Designer.cs 2.Form1.Designer.cs 3.Form1.cs 一般情况下&#xff0c;在标题栏中按住鼠标左键不放即可实现拖动操作。 当做浮动窗体时&#xff0c;如果包含窗体边框&#xff0c;那么界面给使用者的感…

深度学习中的子空间、线性变换和矩阵概念应用

1.表示子空间 在深度学习中&#xff0c;“不同的表示子空间”通常是指模型通过不同的参数&#xff08;例如权重矩阵&#xff09;将输入数据映射到不同的高维空间&#xff0c;这些空间被称为表示子空间。每个子空间都能够捕获输入数据中不同的特征或模式。以下是一些详细解释&am…

php视图处理类

#简介 视图模版替换支持变量,for循环,foreach循环,if判断,if else判断 #调用 index.php <?php use ppt\tool\View; $haha 12; $id 111; $arr [x,y,3]; $pp [x>[id>1,name>12],y>[id>1,name>12],z>[id>1,name>12], ]; echo View::display…

Quarto Dashboards 教程 2:Dashboard Layout

「写在前面」 学习一个软件最好的方法就是啃它的官方文档。本着自己学习、分享他人的态度&#xff0c;分享官方文档的中文教程。软件可能随时更新&#xff0c;建议配合官方文档一起阅读。推荐先按顺序阅读往期内容&#xff1a; 1.quarto 教程 1&#xff1a;Hello, Quarto 2.qu…

diskMirror-backEnd-spring-boot | diskMirror 后端服务器 SpringBoot 版本!

diskMirror-backEnd-spring-boot diskMirror 后端服务器的 SpringBoot 版本&#xff0c;此版本中拓展了 DiskMirrorBackEnd&#xff0c;是一个完全的SpringBoot项目&#xff01; 目录 文章目录 diskMirror-backEnd-spring-boot目录我如何部署与配置docker 方式部署 diskMirro…

ctfshow web入门 SQl注入 web185--web190

web185 这道题还有另外一个脚本就是用concat的拼接达到有数字的目的 concat(truetrue) 2 concat(true) 1 concat(true, true) 11 然后上脚本&#xff08;Y4tacker这个师傅的&#xff09; # Author:Y4tacker import requestsurl "http://341e93e1-a1e7-446a-b7fc-75beb…

【threejs教程9】threejs加载360全景图(VR)的两种方法

目录 前言 1.Equirectangular映射背景 实现步骤 1.初始化TextureLoader 2.加载Equirectangular纹理 3.配置映射类型和颜色空间 4.应用背景 完整代码如下 2.立方体贴图背景 实现步骤 1.创建CubeTextureLoader 2.加载立方体贴图 3.应用背景 完整代码如下 …

从国内盲盒小程序看国外市场的发展机遇与挑战

近年来&#xff0c;盲盒小程序在国内市场迅速崛起&#xff0c;凭借其独特的营销模式和消费者体验&#xff0c;赢得了大量年轻消费者的喜爱。从国内盲盒小程序的发展中&#xff0c;我们可以窥见国外市场的一些发展机遇与挑战&#xff0c;为进军国际市场提供有益的参考。 首先&a…

深入了解Eureka:微服务架构中的服务发现与注册中心

引言 微服务架构的兴起使得应用程序变得更加模块化和可扩展。在这种架构下&#xff0c;服务发现与注册中心扮演着至关重要的角色。本文将深入探讨Eureka作为服务发现与注册中心的作用、优缺点、重要性以及其服务架构。 一、Eureka的作用 Eureka是Netflix开源的一款用于构建分…

MATLAB线性函数拟合并预测

线性函数拟合&#xff0c;由线性函数很好描述的一个数集,也就是说如果我们所考虑的数据是以y(x)的形式给出&#xff0c;并且其中f(x)满足: 要求得 m 和b的值&#xff0c;我们可以使用一个称为 polyii(x,y,n)的 MATLAB 函数&#xff0c;其中n是我们要 MATLAB 求出的多项式的次数…

微软ML Copilot框架释放机器学习能力

摘要&#xff1a;大模型席卷而来&#xff0c;通过大量算法模型训练推理&#xff0c;能根据人类输入指令产生图文&#xff0c;其背后是大量深度神经网络模型在做运算&#xff0c;这一过程称之为机器学习&#xff0c;本文从微软语言大模型出发&#xff0c;详解利用大型语言模型&a…

实现自定义SpringBoot的Starter

starter机制 Spring Boot Starter机制是Spring Boot项目中的一个重要概念&#xff0c;它主要用于简化Maven或Gradle等构建工具中的依赖管理。每个Starter都包含了实现特定功能所需的库和组件&#xff0c;以及相应的配置文件。开发者只需在项目中引入相应的Starter依赖&#xf…

HTTP 网络协议请求的消息结构,具体详解(2024-04-25)

一、简介 HTTP 是基于客户端/服务端&#xff08;C/S&#xff09;的架构模型&#xff0c;通过一个可靠的链接来交换信息&#xff0c;是一个无状态的请求/响应协议。 HTTP 消息是客户端和服务器之间通信的基础&#xff0c;它们由一系列的文本行组成&#xff0c;遵循特定的格式和…