QEMU 总线设备 Device 源码分析

发布时间:2026/9/6 5:00:06

QEMU 总线设备 Device 源码分析
摘要本文从源码层面剖析 QEMU 总线设备 Device 的实现机制。首先介绍 DeviceState、BusState 等核心数据结构及其构成的设备树随后梳理设备从类型注册、实例化到 realize 的完整生命周期并深入分析设备与总线的连接机制、属性系统以及热插拔实现。通过理解这些底层原理读者可以更清晰地掌握 QEMU 设备模型的架构为设备开发、调试与性能优化打下基础。1. 引言QEMU 作为一款功能强大的开源模拟器其设备模型是连接虚拟硬件与真实硬件的桥梁。在 QEMU 的设备模型中总线Bus和设备Device是两个核心抽象理解它们的实现原理对于深入掌握 QEMU 的架构、进行设备开发与调试具有重要意义。本文将从源码层面剖析 QEMU 总线设备 Device 的实现机制帮助读者建立清晰的整体认知。2. 核心数据结构QEMU 的设备模型建立在几个关键的数据结构之上它们共同构成了设备树的骨架。理解这些结构之间的关系是阅读源码的第一步。2.1 DeviceState 结构体DeviceState 是 QEMU 中所有设备的基类定义在 include/hw/qdev-core.h 中。它包含了设备的基本属性、状态、以及与之关联的总线和父设备信息。struct DeviceState { const char *id; DeviceClass *class; BusState *parent_bus; QLIST_HEAD(, BusState) child_bus; QLIST_ENTRY(DeviceState) sibling; int instance_id_alias; int alias_required_for_version; bool realized; bool pending_deleted_event; QemuOpts *opts; int hotplugged; bool allow_unplug_during_migration; BusState *parent_bus; ... };其中realized 字段标识设备是否已完成实例化parent_bus 指向设备挂载的总线child_bus 链表则记录了该设备下挂载的子总线。2.2 BusState 结构体BusState 是总线的基类定义在 include/hw/qdev-core.h 中。总线负责管理挂载在其上的设备并提供设备枚举和访问接口。struct BusState { Object obj; DeviceState *parent; const char *name; BusClass *class; QLIST_HEAD(, DeviceState) children; QLIST_ENTRY(BusState) sibling; int max_index; bool realized; ... };children 链表保存了挂载在该总线上的所有设备parent 指向总线所属的父设备。通过 parent 和 childrenQEMU 构建了一棵完整的设备树。2.3 设备树结构QEMU 中的设备树以机器Machine为根向下逐级展开。每个设备可以拥有多个子总线每条总线又可以挂载多个子设备形成层次化的树形结构。这种结构与 Linux 设备模型中的 device tree 思想一脉相承。flowchart TD A[Machine] -- B[Bus 0] B -- C[Device A] B -- D[Device B] C -- E[Bus 1] E -- F[Device C] E -- G[Device D]3. 设备注册与实例化流程设备的生命周期从类型注册开始经过实例化、连接总线、最终完成 realize 过程。下面逐步分析这一流程。3.1 类型注册QEMU 使用 TypeInfo 结构描述设备类型并通过 type_init 宏在初始化阶段完成注册。设备类型通常继承自 TYPE_DEVICE即 DeviceClass。static const TypeInfo my_device_info { .name TYPE_MY_DEVICE, .parent TYPE_DEVICE, .instance_size sizeof(MyDeviceState), .class_init my_device_class_init, .instance_init my_device_instance_init, }; static void my_device_register_types(void) { type_register_static(my_device_info); } type_init(my_device_register_types);3.2 实例化过程当 QEMU 启动并解析命令行参数后会调用 qdev_create 或 object_new 创建设备实例。qdev_create 是传统接口它接受总线指针和设备类型名完成设备对象的创建并挂载到指定总线上。DeviceState *qdev_create(BusState *bus, const char *name) { DeviceState *dev; dev DEVICE(object_new(name)); qdev_set_parent_bus(dev, bus); return dev; }qdev_set_parent_bus 将设备的 parent_bus 指向目标总线同时将设备加入总线的 children 链表完成设备与总线的关联。3.3 realize 流程设备创建后需要调用 qdev_init_nofail 或 device_ realize 完成最终初始化。realize 阶段会调用设备类中定义的 realize 回调执行具体的硬件初始化逻辑。void qdev_init_nofail(DeviceState *dev) { Error *err NULL; object_property_set_bool(OBJECT(dev), realized, true, err); if (err) { error_reportf_err(err, Initialization of device %s failed: , object_get_typename(OBJECT(dev))); exit(1); } }在 realize 回调中设备通常会注册 IO 内存区域、初始化中断、创建子总线等。例如一个 PCI 网卡设备会在 realize 中调用 pci_register_bar 注册 BAR 空间。4. 总线设备连接机制设备与总线的连接是设备模型的核心操作。QEMU 提供了多种方式建立这种连接包括显式的 qdev_set_parent_bus 调用以及通过设备属性自动匹配。4.1 总线匹配规则每条总线都有其类型设备在挂载时需要满足总线类型兼容性。例如PCI 设备只能挂载到 PCI 总线上。QEMU 通过 BusClass 的 device_class 检查机制来保证这一点。static void qdev_set_parent_bus(DeviceState *dev, BusState *bus) { BusClass *bus_class BUS_GET_CLASS(bus); DeviceClass *dev_class DEVICE_GET_CLASS(dev); ... if (bus_class-device_class) { if (!object_dynamic_cast(OBJECT(dev), bus_class-device_class)) { error_report(Device %s is not a valid %s device, object_get_typename(OBJECT(dev)), bus_class-device_class); exit(1); } } ... }4.2 设备枚举与查找QEMU 提供了 qdev_find_recursive 等接口用于在设备树中按名称查找设备。该函数从指定总线出发递归遍历所有子总线和子设备。DeviceState *qdev_find_recursive(BusState *bus, const char *name) { DeviceState *dev, *ret; BusState *child; QLIST_FOREACH(dev, bus-children, sibling) { if (strcmp(dev-id, name) 0) { return dev; } QLIST_FOREACH(child, dev-child_bus, sibling) { ret qdev_find_recursive(child, name); if (ret) { return ret; } } } return NULL; }5. 设备属性系统QEMU 设备模型通过属性Property系统实现设备参数的配置与访问。属性可以是整数、字符串、指针等类型通过统一的接口进行读写。5.1 属性定义设备类在 class_init 中通过 DEFINE_PROP_* 宏定义属性。例如定义一个整数属性和一个字符串属性static Property my_device_properties[] { DEFINE_PROP_UINT32(irq, MyDeviceState, irq, 0), DEFINE_PROP_STRING(name, MyDeviceState, name), DEFINE_PROP_END_OF_LIST(), }; static void my_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc DEVICE_CLASS(klass); dc-props my_device_properties; ... }5.2 属性访问属性通过 object_property_set_int 和 object_property_get_int 等接口进行访问。这些接口最终会调用属性对应的 getter 和 setter 回调。object_property_set_int(OBJECT(dev), irq, 5, err); int irq object_property_get_int(OBJECT(dev), irq, err);6. 热插拔实现热插拔是设备模型的重要功能允许在系统运行期间动态添加或移除设备。QEMU 通过总线类中的 hotplug 回调实现这一机制。6.1 热插拔接口BusClass 中定义了 hotplug 和 hot_unplug 回调设备模型在热插拔操作时调用这些回调。struct BusClass { ... void (*hotplug)(BusState *bus, DeviceState *dev, Error **errp); void (*hot_unplug)(BusState *bus, DeviceState *dev, Error **errp); ... };6.2 热插拔流程以 PCI 设备热插拔为例当用户通过管理接口请求添加设备时QEMU 会调用 pci_bus_hotplug 回调完成设备创建、资源分配和 guest 通知等操作。static void pci_bus_hotplug(BusState *bus, DeviceState *dev, Error **errp) { PCIDevice *pci_dev PCI_DEVICE(dev); ... pci_dev-bus-devices[pci_dev-devfn] pci_dev; ... qdev_set_parent_bus(dev, bus); ... }7. 总结本文从源码层面分析了 QEMU 总线设备 Device 的核心实现机制。通过剖析 DeviceState、BusState 等关键数据结构梳理了设备注册、实例化、realize 的完整流程并深入探讨了设备与总线的连接机制、属性系统以及热插拔实现。理解这些底层原理对于进行 QEMU 设备开发、调试和性能优化具有重要的指导意义。QEMU 的设备模型设计精巧层次清晰其面向对象的思想和灵活的扩展机制值得深入学习。建议读者结合具体设备如 PCI 网卡、virtio 设备的源码进一步加深对设备模型的理解。

相关新闻

2026年汕头手信店精选指南:哪家最值得信赖?

2026年汕头手信店精选指南:哪家最值得信赖?

2026/9/6 5:00:06

随着旅游与文化的交融日益加深,挑选一份地道的手信成为旅行中不可或缺的一部分。而在众多手信店铺中做出选择,并非易事。本文将重点推荐一家在汕头备受赞誉的特产店——拾青潮汕特产(龙眼南路店),并提供一些实用建议&a…

Python学习记录12

Python学习记录12

2026/9/6 5:00:06

今天学习了关于 set 集合的一些知识和基本操作步骤,现摘录如下:#set的基本操作(无序,不可重复,不可修改) #定义 s {a,a,b,c,d,e,f,g,h,i} #空集合定义 #s1 set{}这样的定义是字典 #由于集合无序,因此不支持下标索引访…

基于Python和Vue3的学科竞赛管理系统毕业设计全解析

基于Python和Vue3的学科竞赛管理系统毕业设计全解析

2026/9/6 5:00:06

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

同人动画制作全流程解析:从MLP案例学习2D动画技术实践

同人动画制作全流程解析:从MLP案例学习2D动画技术实践

2026/9/6 6:00:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

通信协议不等于调用库函数:从帧格式到状态机的嵌入式通信设计指南

通信协议不等于调用库函数:从帧格式到状态机的嵌入式通信设计指南

2026/9/6 6:00:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

从零到第一百个链接:新店铺的冷启动产能路线

从零到第一百个链接:新店铺的冷启动产能路线

2026/9/6 6:00:08

从零到第一百个链接:新店铺的冷启动产能路线 一个新店主的百日困惑: 「新店前一百天,我听得最多的话是『先上链接』。可没人告诉我一百个链接对我意味着什么:按手工十分钟一个,那是十六七个小时的纯操作——还不算素材…

Python入门不半途而废:从环境搭建到实战项目的完整学习路径

Python入门不半途而废:从环境搭建到实战项目的完整学习路径

2026/9/6 6:00:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

量化交易基础之python库学习

量化交易基础之python库学习

2026/9/6 6:00:08

一 股票常用指标极差:越高说明波动越明显极差,股价近期最高价的最大值和最小值的差值成交量加权平均价格:英文名VWAP(Volume-Weighted Average Price,成交成交量加权平均价格量加权平均价格)是一个非常重要的经济学量,…

三江并流年度报告编制指南:从数据采集到成效解读

三江并流年度报告编制指南:从数据采集到成效解读

2026/9/6 5:50:08

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

远程协作的工作台整理

远程协作的工作台整理

2026/9/3 6:56:24

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/4 7:42:10

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/5 23:14:13

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…