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

系列文章目录

第十七章 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.mzph.cn/news/828816.shtml

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

相关文章

4.25java项目小结

完成了头像的显示&#xff0c;能将头像设置圆形&#xff0c;从数据库传输头像&#xff0c;客户端接收并在界面上展示&#xff0c;并能从文件选择图片 、

Golang | Leetcode Golang题解之第49题字母异位词分组

题目&#xff1a; 题解&#xff1a; func groupAnagrams(strs []string) [][]string {mp : map[[26]int][]string{}for _, str : range strs {cnt : [26]int{}for _, b : range str {cnt[b-a]}mp[cnt] append(mp[cnt], str)}ans : make([][]string, 0, len(mp))for _, v : ra…

金融级国产化替代中间件有哪些?

过去&#xff0c;国内中间件市场一直由IBM、Oracle等国际大型企业所主导&#xff0c;这在一定程度上限制了对国内企业多样化和个性化需求的满足&#xff0c;尤其是在实现底层硬件与上层应用软件之间高效、精准匹配方面。面对日益复杂的国际局势&#xff0c;金融安全已成为国家整…

负载均衡集群——LVS

目录 1.LVS简介 2.LVS体系结构 3.LVS相关术语 4. LVS工作模式 5. LVS调度算法 6.LVS集群介绍 6.1 LVS-DR模式 6.2 LVS – NAT 模式 6.3 LVS – TUN 模式 7.LVS 集群构建 7.1 LVS/NAT 模式配置 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤 2 安装和配置 LVS …

【AIGC调研系列】Bunny-Llama-3-8B-V与其他多模态大模型相比的优劣

Bunny-Llama-3-8B-V作为基于Llama-3的多模态大模型&#xff0c;其优势主要体现在以下几个方面&#xff1a; 性能超越其他模型&#xff1a;根据我搜索到的资料&#xff0c;Bunny-Llama-3-8B-V在多个主流Benchmark上表现良好&#xff0c;超越了LLaVA-7B、LLaVA-13B、Mini-Gemini…

Navicat连接SQLSever报错:[08001] MicrosoftTCP Provider 远程主机强迫关闭了一个现有的连接

Navicat连接SQLSever报错&#xff1a;[08001] [Microsoft][SQL Server Native Client 10.0]TCP Provider: 远程主机强迫关闭了一个现有的连接 问题分析 旧版的MSSQL 如果不是最新版的&#xff0c;可以去这安装以下即可。 最新版的MSSQL 如果是安装最新版的MSSQL连接不上很正…

2024高级卫生职称考试报名时间汇总

20地报名时间汇总&#xff0c;其他时间安排见图 上海&#xff1a;4.23-5.24 黑龙江&#xff1a;4.23-5.24 陕西&#xff1a;4.23-5.24 重庆&#xff1a;4.23-5.24 浙江&#xff1a;4.23-5.24 20地报名时间汇总 甘肃&#xff1a;4.23-5.24 江西&#xff1a;4.28-5.10 河北&#…

【黑马点评Redis——004达人探店】

1.发布探店笔记 2.点赞 利用Redis中的Set集合来判断是否点赞过。 3.点赞排行榜 可以通过SortedSet来按点赞时间进行排序。 4.好友关注 4.1.关注和取关 4.2.共同关注 可以通过set实现交集的功能 4.3.关注推送 4.3.1 拉模式 拉模式&#xff08;Pull&#xff09;&#x…

linux 上 jps 列出一堆 jar,如何快速定位 jar 文件启动位置?

例如&#xff0c;在 /data下有一个 xxx.jar &#xff0c;如果是通过 "java -jar /data/xxx.jar" 方式启动&#xff0c;则 jps会列出的名字中带 xxx.jar&#xff0c;这时再 "ps -ef | grep xxx.jar" 就会列出 更详细的信息&#xff0c;例如 "java -ja…

【VScode】VScode+如何从git上面拉取代码?

目录标题 1、打开VSCode。File>New Window。2、打开集成终端&#xff08;Terminal > New Terminal 或使用快捷键Ctrl \)。3、在终端中&#xff0c;使用Git命令克隆仓库。4、打开项目。 1、打开VSCode。File>New Window。 2、打开集成终端&#xff08;Terminal > …

Mongodb语法使用说明(含详细示例)

点击下载《Mongodb语法使用说明&#xff08;含详细示例&#xff09;》 1. 前言 MongoDB是一款高性能、开源、面向文档的NoSQL数据库&#xff0c;它使用类似JSON的BSON格式存储数据&#xff0c;提供了灵活的数据模型和强大的查询功能。本文将详细介绍MongoDB数据库的基本增删改…

vue快速入门(四十四)自定义组件

注释很详细&#xff0c;直接上代码 上一篇 新增内容 全局注册自定义组件并应用局部注册自定义组件并应用 此篇使用了axios模块没有安装导入的先看这一篇 axios模块下载与导入 源码 main.js import Vue from vue import App from ./App.vue//全局引入axios // 引入axios impor…

HTTP与HTTPS 对比,区别详解(2024-04-25)

一、简介 HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上&#xff0c;通常使用端口 80。 HTTPS&#xf…

【自然语言处理】InstructGPT、GPT-4 概述

InstructGPT官方论文地址&#xff1a;https://arxiv.org/pdf/2203.02155.pdf GPT-4 Technical Report&#xff1a;https://arxiv.org/pdf/2303.08774.pdf GPT-4&#xff1a;GPT-4 目录 1 InstructGPT 2 GPT-4 1 InstructGPT 在了解ChatGPT之前&#xff0c;我们先看看Instr…

OpenHarmony开源软件供应链安全风险

慕冬亮&#xff0c;华中科技大学网络空间安全学院副教授&#xff0c;武汉英才&#xff0c;华中科技大学OpenHarmony技术俱乐部、开放原子开源社团指导教师。研究方向为软件与系统安全&#xff0c;在国际安全会议上发表十余篇论文&#xff0c;并获得ACM CCS 2018杰出论文奖。创立…

【研发管理】产品经理知识体系-产品创新中的市场调研

导读&#xff1a;在产品创新过程中&#xff0c;市场调研的重要性不言而喻。它不仅是产品创新的起点&#xff0c;也是确保产品成功推向市场的关键步骤。对于产品经理系统学习和掌握产品创新中的市场调研相关知识体系十分重要。 目录 概述&#xff1a;市场调研重要性 1、相关概…

YoloV8改进策略:卷积改进|DOConv轻量卷积,即插即用|适用各种场景

摘要 本文使用DOConv卷积,替换YoloV8的常规卷积,轻量高效,即插即用!改进方法非常简单。 DO-Conv(Depthwise Over-parameterized Convolutional Layer)是一种深度过参数化的卷积层,用于提高卷积神经网络(CNN)的性能。它的核心思想是在训练阶段使用额外的深度卷积来增…

【C++题解】1037. 恐龙园买门票

问题&#xff1a;1037. 恐龙园买门票 类型&#xff1a;分支 题目描述&#xff1a; 恐龙园买门票&#xff0c;身高低于 1.3 米购儿童票( 60元 )&#xff0c;否则成人票 120 元。 试编写一个程序&#xff0c;输入身高&#xff0c;输出相应的门票价格。 输入&#xff1a; 一行…

OpenCV鼠标绘制线段

鼠标绘制线段 // 鼠标回调函数 void draw_circle(int event, int x, int y, int flags, void* param) {cv::Mat* img (cv::Mat*)param;if (event cv::EVENT_LBUTTONDBLCLK){cv::circle(*img, cv::Point(x, y), 100, cv::Scalar(0, 0, 255), -1);} }// 鼠标回调函数 void dra…

阿斯达年代记游戏下载教程 阿斯达年代记下载教程

《阿斯达年代记&#xff1a;三强争霸》作为一款气势恢宏的MMORPG大作&#xff0c;是Netmarble与STUDIO DRAGON强强联合的巅峰创作&#xff0c;定于4月24日迎来全球玩家热切期待的公测。游戏剧情围绕阿斯达大陆的王权争夺战展开&#xff0c;三大派系——阿斯达联邦、亚高联盟及边…