【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

相关文章

nginx启动命令和默认配置文件的使用

《nginx启动命令和默认配置文件的使用》:本文主要介绍nginx启动命令和默认配置文件的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录常见命令nginx.conf配置文件location匹配规则图片服务器总结常见命令# 默认配置文件启动./nginx

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4

Nexus安装和启动的实现教程

《Nexus安装和启动的实现教程》:本文主要介绍Nexus安装和启动的实现教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Nexus下载二、Nexus安装和启动三、关闭Nexus总结一、Nexus下载官方下载链接:DownloadWindows系统根

Java中实现线程的创建和启动的方法

《Java中实现线程的创建和启动的方法》在Java中,实现线程的创建和启动是两个不同但紧密相关的概念,理解为什么要启动线程(调用start()方法)而非直接调用run()方法,是掌握多线程编程的关键,... 目录1. 线程的生命周期2. start() vs run() 的本质区别3. 为什么必须通过 st

Oracle修改端口号之后无法启动的解决方案

《Oracle修改端口号之后无法启动的解决方案》Oracle数据库更改端口后出现监听器无法启动的问题确实较为常见,但并非必然发生,这一问题通常源于​​配置错误或环境冲突​​,而非端口修改本身,以下是系... 目录一、问题根源分析​​​二、保姆级解决方案​​​​步骤1:修正监听器配置文件 (listener.

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

MySQL启动报错:InnoDB表空间丢失问题及解决方法

《MySQL启动报错:InnoDB表空间丢失问题及解决方法》在启动MySQL时,遇到了InnoDB:Tablespace5975wasnotfound,该错误表明MySQL在启动过程中无法找到指定的s... 目录mysql 启动报错:InnoDB 表空间丢失问题及解决方法错误分析解决方案1. 启用 inno

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失

解决tomcat启动时报Junit相关错误java.lang.ClassNotFoundException: org.junit.Test问题

《解决tomcat启动时报Junit相关错误java.lang.ClassNotFoundException:org.junit.Test问题》:本文主要介绍解决tomcat启动时报Junit相... 目录tomcat启动时报Junit相关错误Java.lang.ClassNotFoundException

Redis在windows环境下如何启动

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