当前位置: 首页 > news >正文

qemu学习笔记:QOM

2.4 QOM介绍

说明:小白学习qemu的一些学习笔记。主要是学习《QEMU&KVM源码解析与应用》这本书。

参考:

《QEMU&KVM源码解析与应用》作者:李强

Qemu - 百问网嵌入式Linux wiki

  • QOM 定义:QEMU Object Model,是 QEMU 中对对象的抽象层,用于管理和抽象资源(如设备创建、配置、销毁)及后端组件(如 MemoryRegion、Machine 等)。
  • 面向对象思想:继承、封装、多态。尽管 QEMU 和 Linux 内核是 C 语言项目,仍充分运用这些思想,QOM 是 QEMU 中具体体现。
  • C 语言实现对象:通过父类抽象具体对象(如网卡、显卡等)。例:PCI 设备类的父类是设备类,体现继承。
  • 类型与对象区别
    • 类型:表示 “种类”(如 edu 是种类)。
    • 对象:表示该种类中的具体实例(如 edu1edu2edu 类型的具体对象)。
  • QOM 组成部分
    • 类型的注册
    • 类型的初始化
    • 对象的初始化
┌───────────────┐     ┌───────────────┐     ┌───────────────┐  
│ 类型注册        │ →   │ 类型初始化      │ →   │ 对象初始化     │  
└───────────────┘     └───────────────┘     └───────────────┘  

该框图表示 QOM 的核心流程,从类型注册开始,经过类型初始化,最终完成对象初始化,体现 QOM 对对象管理的逻辑顺序。

image-20250401235826723

image-20250402204805726
2.4.1 类型的注册

书上的内容就不进行复制了,主要关注的是代码。这一小节主要是type_init()完成注册的过程。先对type_init()进行追踪。介绍了从信息填写到注册再到qemu运行时进入main()之后完成注册过程。就是分析下面这段代码的过程。

static void pci_edu_register_types(void)
{// 信息填写static const TypeInfo edu_info = {.name          = "edu",.parent        = TYPE_PCI_DEVICE,.instance_size = sizeof(EduState),.instance_init = edu_instance_init,.class_init    = edu_class_init,};type_register_static(&edu_info);
}
// 注册
type_init(pci_edu_register_types)

从type_init()开始分析,这是一个宏,用于调用module_init()的时候区分不同的类型

/*
block_init:    块设备(如磁盘等存储相关模块)初始化相关的函数,与 MODULE_INIT_BLOCK 类型绑定
opts_init:     选项(如命令行参数解析、配置选项处理等模块)初始化相关
qapi_init:     QAPI(QEMU Abstract Interface,QEMU 抽象接口,用于定义命令、数据结构等)
type_init:     QOM(QEMU Object Model,QEMU 对象模型,用于抽象和管理设备、资源等)相关
trace_init:    跟踪(如调试跟踪、事件跟踪等模块)相关
*/
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#define opts_init(function) module_init(function, MODULE_INIT_OPTS)
#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
#define type_init(function) module_init(function, MODULE_INIT_QOM)
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)

module_init()也是一个宏,跳转到register_module_init()完成注册

/* This should not be used directly.  Use block_init etc. instead.  */
#define module_init(function, type)                                         \
static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \register_module_init(function, type);                                   \
}
#endiftypedef struct ModuleEntry
{void (*init)(void);                 // 类型的初始化函数 这里就是 pci_edu_register_typesQTAILQ_ENTRY(ModuleEntry) node;     // 双向链表module_init_type type;              // 类型  这里就是MODULE_INIT_QOM
} ModuleEntry;// 返回init_type_list数组 MODULE_INIT_QOM类型对应的一项
static ModuleTypeList *find_type(module_init_type type)
{init_lists();return &init_type_list[type];
}void register_module_init(void (*fn)(void), module_init_type type)
{ModuleEntry *e;ModuleTypeList *l;e = g_malloc0(sizeof(*e));e->init = fn;e->type = type;// 完成ModuleEntry结构体设置// 获取链表的开头,或者说init_type_list数组 MODULE_INIT_QOM项l = find_type(type);// 链表插入QTAILQ_INSERT_TAIL(l, e, node);
}

这段代码:是register_module_init()函数,把需要注册的类型封装成对应的类型结构体ModuleEntry,插入到对应的链表当中,这些不同类型的链表都在一个叫init_type_list数组里面放着。

在这里插入图片描述

下面就是QEMU运行的时候,进入main()后不久,就会调用module_call_init(MODULE_INIT_QOM),遍历MODULE_INIT_QOM对应的链表,挨个执行init()函数,在例子当中就是pci_edu_register_types()函数。

void module_call_init(module_init_type type)
{ModuleTypeList *l;ModuleEntry *e;l = find_type(type);QTAILQ_FOREACH(e, l, node) {e->init();}
}

接下来就看pci_edu_register_types()函数,这个函数首先TypeInfo结构体去承接了一些类型信息。然后将TypeInfo结构体传给type_register_static()函数。

static void pci_edu_register_types(void)
{static const TypeInfo edu_info = {.name          = "edu",.parent        = TYPE_PCI_DEVICE,.instance_size = sizeof(EduState),.instance_init = edu_instance_init,.class_init    = edu_class_init,};type_register_static(&edu_info);
}

又经过封装最后到了type_register_internal()函数,在此次完成两件事

  • type_new():将TypeInfo类型----->TypeImpl类型

  • type_table_add():将 TypeImpl 装入哈希表,以TypeImpl的名字为key,以TypeImpl的本身值为value。

type_register_static(&edu_info);----->type_register_internal()// 完成最终注册,转成TypeImpl加入哈希表
static TypeImpl *type_register_internal(const TypeInfo *info)
{TypeImpl *ti;ti = type_new(info);type_table_add(ti);return ti;
}// 加入哈希表,以名字为key
static void type_table_add(TypeImpl *ti)
{assert(!enumerating_types);g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
}// TypeInfo---》》》TypeImpl
static TypeImpl *type_new(const TypeInfo *info)
{TypeImpl *ti = g_malloc0(sizeof(*ti));int i;g_assert(info->name != NULL);if (type_table_lookup(info->name) != NULL) {fprintf(stderr, "Registering `%s' which already exists\n", info->name);abort();}ti->name = g_strdup(info->name);ti->parent = g_strdup(info->parent);ti->class_size = info->class_size;ti->instance_size = info->instance_size;ti->class_init = info->class_init;ti->class_base_init = info->class_base_init;ti->class_finalize = info->class_finalize;ti->class_data = info->class_data;ti->instance_init = info->instance_init;ti->instance_post_init = info->instance_post_init;ti->instance_finalize = info->instance_finalize;ti->abstract = info->abstract;for (i = 0; info->interfaces && info->interfaces[i].type; i++) {ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);}ti->num_interfaces = i;return ti;
}

到这里,完成了注册过程,类型信息从一开始的TypeInfo类型,转成了TypeImpl,并且加载到了一个哈希表里面,通过TypeImpl的名字可以得到类型的全部信息。在后面的实例化中使用。

2.4.2 类型的初始化

先观察TypeInfo里面的内容有两个初始化函数,edu_class_init类初始化和edu_instance_init实例对象初始化。实例化有两种方式在上面的图中,底层实现顺序基本上一致。

ti->class_init(ti->class, ti->class_data); 类初始化V
ti->instance_init(obj) : 实例对象初始化V
实例化:realized属性设置为ture
    static const TypeInfo edu_info = {.name          = "edu",.parent        = TYPE_PCI_DEVICE,.instance_size = sizeof(EduState),.instance_init = edu_instance_init,.class_init    = edu_class_init,};

在这里插入图片描述

这一小节主要是针对类型的初始化,分析type_initialize函数对类型进行初始化:调用ti->class_init(ti->class, ti->class_data)的过程。

  • 在 C++ 等面向对象编程语言中,编译器在声明类型时,能直接知晓类型的相关信息,像对象大小等,这是语言内置的面向对象机制支持。而 C 语言本身不是面向对象语言,没有这种内置支持,要实现类似面向对象特性(如类、对象等概念 ),就得做特殊处理。
  • 通过这个函数来手动处理 C 语言中类的初始化过程,弥补 C 语言在面向对象支持上的缺失,实现类型相关信息的设置、内存分配等初始化操作。

type_initialize函数为了模拟C++的面向对象,主要做了三件事情:

  1. 设置类结构大小、ti 所代表类型的对象实例的大小。
  2. 初始化所有父类类型
  3. 依次调用所有父类的 class_base_init 以及自己的 class_init,这也和 C++ 很类似,在初始化一个对象的时候会依次调用所有父类的构造函数。这里是调用了父类型的 class_base_init 函数。

type_initialize函数调用场景灵活,但具备 “一次性初始化” 特性。首次调用时,因ti->class为空(表示类型尚未初始化 ),会执行初始化流程 。后续再调用,ti->class已被赋值非空,函数直接返回,不再重复初始化过程。这一设计可避免对同一类型多次不必要的初始化,提高程序执行效率,保证类型初始化的一致性和准确性 。

static void type_initialize(TypeImpl *ti)
{TypeImpl *parent;// 先检查ti->class是否存在,若已初始化就直接返回。/*type_initialize函数调用场景灵活,但具备 “一次性初始化” 特性。首次调用时,因ti->class为空(表示类型尚未初始化 ),会执行初始化流程,完成如设置相关尺寸、分配内存、调用父类及自身初始化函数等操作 。后续再调用,ti->class已被赋值非空,函数直接返回,不再重复初始化过程。这一设计可避免对同一类型多次不必要的初始化,提高程序执行效率,保证类型初始化的一致性和准确性 。*/if (ti->class) {return;}// 1、设置类结构大小、ti 所代表类型的对象实例的大小。ti->class_size = type_class_get_size(ti);ti->instance_size = type_object_get_size(ti);ti->class = g_malloc0(ti->class_size);// 2、初始化所有父类类型parent = type_get_parent(ti);if (parent) {type_initialize(parent);GSList *e;int i;g_assert_cmpint(parent->class_size, <=, ti->class_size);memcpy(ti->class, parent->class, parent->class_size);ti->class->interfaces = NULL;ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, object_property_free);for (e = parent->class->interfaces; e; e = e->next) {InterfaceClass *iface = e->data;ObjectClass *klass = OBJECT_CLASS(iface);type_initialize_interface(ti, iface->interface_type, klass->type);}for (i = 0; i < ti->num_interfaces; i++) {TypeImpl *t = type_get_by_name(ti->interfaces[i].typename);for (e = ti->class->interfaces; e; e = e->next) {TypeImpl *target_type = OBJECT_CLASS(e->data)->type;if (type_is_ancestor(target_type, t)) {break;}}if (e) {continue;}type_initialize_interface(ti, t, t);}} else {ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, object_property_free);}ti->class->type = ti;// 3、初始化对象之前需要依次调用父类的构造函数,//    这里是调用父类的class_base_init函数while (parent) {if (parent->class_base_init) {parent->class_base_init(ti->class, ti->class_data);}parent = type_get_parent(parent);}if (ti->class_init) {ti->class_init(ti->class, ti->class_data);}
}

到这里,注册完毕,类型初始化完成,还有对象初始化和实例化没有做。

2.4.3 类型的层次结构
2.4.4 对象的构造与初始化

本节主要是说明对象初始化。对应的是下图调用instance_init()

在这里插入图片描述

运行流程为

// 1、从名字找到TypeImpl,进入object_new_with_type
Object *object_new(const char *typename)
{TypeImpl *ti = type_get_by_name(typename);return object_new_with_type(ti);
}
// 2、分配对象大小
Object *object_new_with_type(Type type)
{Object *obj;g_assert(type != NULL);// 判断类初始化是否完成type_initialize(type);// 对象分配大小obj = g_malloc(type->instance_size);object_initialize_with_type(obj, type->instance_size, type);obj->free = g_free;return obj;
}// 对对象初始化
void object_initialize_with_type(void *data, size_t size, TypeImpl *type)
{Object *obj = data;g_assert(type != NULL);type_initialize(type);g_assert_cmpint(type->instance_size, >=, sizeof(Object));g_assert(type->abstract == false);g_assert_cmpint(size, >=, type->instance_size);memset(obj, 0, type->instance_size);obj->class = type->class;object_ref(obj);obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal,NULL, object_property_free);object_init_with_type(obj, type);object_post_init_with_type(obj, type);
}// 递归调用所有父类型的对象初始化函数和自身对象的初始化函数
// 这里是调用父类的instance_init函数
static void object_init_with_type(Object *obj, TypeImpl *ti)
{if (type_has_parent(ti)) {object_init_with_type(obj, type_get_parent(ti));}if (ti->instance_init) {ti->instance_init(obj);}
}
// 调用instance_post_init完成对象初始化之后的工作
static void object_post_init_with_type(Object *obj, TypeImpl *ti)
{if (ti->instance_post_init) {ti->instance_post_init(obj);}if (type_has_parent(ti)) {object_post_init_with_type(obj, type_get_parent(ti));}
}

QOM 对象构造相关要点

  1. 类型的构造(注册)
    • 方式:通过TypeInfo构造一个TypeImpl的哈希表。
    • 时机:在main之前完成。
  2. 类型初始化(先调父类构造,再class_init
    • 进行位置:在main函数中。
    • 作用范围:全局,所有编译进去的 QOM 对象都会调用。
  3. 类对象构造和初始化(调instance_init
    • 构建内容:构造具体的对象实例。
    • 触发条件:仅在命令行指定对应设备时,才会创建对象。
  4. 对象实例化:
    • 以上过程,构造出了对象并调用初始化函数,但是 EduState 里面的数据内容并没有填充,这个时候的 edu 设备状态并不是可用的
    • 对设备而言还需要设置它的 realized 属性为 true 才行
2.4.5 属性

相关文章:

  • 算法--模拟题目
  • C++中常用的十大排序方法之1——冒泡排序
  • 【数据结构】线性表--顺序表
  • 常用机械传动方式对比
  • 【笔记】深度学习模型训练的 GPU 内存优化之旅④:内存交换与重计算的联合优化篇
  • 腾讯云BI VS quickbi 企业选型(从企业实际功能使用和费用对比)
  • 提升采购管理,打造核心竞争力七步战略采购法详解P94(94页PPT)(文末有下载方式)
  • 【Bootstrap V4系列】学习入门教程之 页面内容排版
  • Qwen3 发布:优化编码与代理能力,强化 MCP 支持引领 AI 新潮流
  • PHP之CURL通过header传参数及接收
  • 如何快速定位网络中哪台主机发起ARP攻击
  • 前端八股 6
  • 【Linux】C语言补充知识
  • 西门子数字化研发设计制造一体化规划案例P87(87页PPT)(文末有下载方式)
  • PHP-Cookie
  • 攻防世界 - Misc - Level 6 | Wireshark
  • 字节一面:后端开发
  • 卡洛诗西餐的文化破圈之路
  • 3.2/Q2,Charls最新文章解读
  • SARSA 算法详解:python从零实现
  • 居委业委居民群策群力,7位一级演员来到上海一小区唱戏
  • 澎湃读报丨央媒头版集中刊发社论,庆祝“五一”国际劳动节
  • 耶路撒冷发生山火,以防长宣布紧急状态
  • 七部门联合发布《终端设备直连卫星服务管理规定》
  • 是否进行了及时有效处置?伤者情况如何?辽阳市相关负责人就饭店火灾事故答问
  • 张炜琳已任三明市委常委、宣传部部长