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

2024-04-25 23:28

本文主要是介绍【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系统仿真完成显示初始化的代码。

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



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

相关文章

Redis在windows环境下如何启动

《Redis在windows环境下如何启动》:本文主要介绍Redis在windows环境下如何启动的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis在Windows环境下启动1.在redis的安装目录下2.输入·redis-server.exe

解决SpringBoot启动报错:Failed to load property source from location 'classpath:/application.yml'

《解决SpringBoot启动报错:Failedtoloadpropertysourcefromlocationclasspath:/application.yml问题》这篇文章主要介绍... 目录在启动SpringBoot项目时报如下错误原因可能是1.yml中语法错误2.yml文件格式是GBK总结在启动S

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

一文带你了解SpringBoot中启动参数的各种用法

《一文带你了解SpringBoot中启动参数的各种用法》在使用SpringBoot开发应用时,我们通常需要根据不同的环境或特定需求调整启动参数,那么,SpringBoot提供了哪些方式来配置这些启动参... 目录一、启动参数的常见传递方式二、通过命令行参数传递启动参数三、使用 application.pro

SpringBoot项目启动报错"找不到或无法加载主类"的解决方法

《SpringBoot项目启动报错找不到或无法加载主类的解决方法》在使用IntelliJIDEA开发基于SpringBoot框架的Java程序时,可能会出现找不到或无法加载主类com.example.... 目录一、问题描述二、排查过程三、解决方案一、问题描述在使用 IntelliJ IDEA 开发基于

SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法

《SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法》本文主要介绍了SpringBoot项目启动错误:找不到或无法加载主类的几种解决方法,具有一定的参考价值,感兴趣的可以了解一下... 目录方法1:更改IDE配置方法2:在Eclipse中清理项目方法3:使用Maven命令行在开发Sprin

Nginx启动失败:端口80被占用问题的解决方案

《Nginx启动失败:端口80被占用问题的解决方案》在Linux服务器上部署Nginx时,可能会遇到Nginx启动失败的情况,尤其是错误提示bind()to0.0.0.0:80failed,这种问题通... 目录引言问题描述问题分析解决方案1. 检查占用端口 80 的进程使用 netstat 命令使用 ss

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Windows设置nginx启动端口的方法

《Windows设置nginx启动端口的方法》在服务器配置与开发过程中,nginx作为一款高效的HTTP和反向代理服务器,被广泛应用,而在Windows系统中,合理设置nginx的启动端口,是确保其正... 目录一、为什么要设置 nginx 启动端口二、设置步骤三、常见问题及解决一、为什么要设置 nginx

springboot启动流程过程

《springboot启动流程过程》SpringBoot简化了Spring框架的使用,通过创建`SpringApplication`对象,判断应用类型并设置初始化器和监听器,在`run`方法中,读取配... 目录springboot启动流程springboot程序启动入口1.创建SpringApplicat