QEMU源码全解析41 —— Machine(11)

接前一篇文章:QEMU源码全解析40 —— Machine(10)

本文内容参考:

《趣谈Linux操作系统》 —— 刘超,极客时间

《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社

特此致谢!

时间过去了几个月,重开“Machine”系列……

“Machine”系列的前10篇文章、尤其是从第3篇文章开始,就感觉比较乱,一直在针对于宏定义进行展开,并且头绪众多,给人感觉没有一个清晰的脉络。那么本篇文章就来总结前边几篇文章的内容,并梳理出较为清晰的脉络。

先来看一张图(图片援引《趣谈Linux系统》50 | 计算虚拟化之CPU(上):如何复用集团的人力资源?):

在hw/i386/pc_piix.c中,有核心宏DEFINE_I440FX_MACHINE。对于每一个QEMU版本,都会定义一种新的机器类型,以笔者之前的v7.1以及现在使用的v8.1为例,代码分别如下:

DEFINE_I440FX_MACHINE(v7_1, "pc-i440fx-7.1", NULL,pc_i440fx_7_1_machine_options);
DEFINE_I440FX_MACHINE(v8_1, "pc-i440fx-8.1", NULL,pc_i440fx_8_1_machine_options);

DEFINE_I440FX_MACHINE宏的定义也在hw/i386/pc_piix.c中,如下:

#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \static void pc_init_##suffix(MachineState *machine) \{ \void (*compat)(MachineState *m) = (compatfn); \if (compat) { \compat(machine); \} \pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \TYPE_I440FX_PCI_DEVICE); \} \DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)

对于v7.1和v8.1,最终展开为:

static void pc_init_v7_1(MachineState *machine)
{void (*compat)(MachineState *m) = (NULL);if (compat) {compat(machine);}pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \TYPE_I440FX_PCI_DEVICE);
}static void pc_machine_v7_1_class_init(ObjectClass *oc, void *data)
{MachineClass *mc = MACHINE_CLASS(oc);pc_i440fx_7_1_machine_options(mc);mc->init = pc_init_v7_1;
}
static const TypeInfo pc_machine_type_v7_1 = {.name       = "pc-i440fx-7.1" TYPE_MACHINE_SUFFIX,.parent     = TYPE_PC_MACHINE,.class_init = pc_machine_v7_1_class_init,
};
static void pc_machine_init_v7_1(void)
{type_register(&pc_machine_type_v7_1);
}
type_init(pc_machine_init_v7_1)

static void pc_init_v8_1(MachineState *machine)
{void (*compat)(MachineState *m) = (NULL);if (compat) {compat(machine);}pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \TYPE_I440FX_PCI_DEVICE);
}static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
{MachineClass *mc = MACHINE_CLASS(oc);pc_i440fx_8_1_machine_options(mc);mc->init = pc_init_v8_1;
}
static const TypeInfo pc_machine_type_v8_1 = {.name       = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,.parent     = TYPE_PC_MACHINE,.class_init = pc_machine_v8_1_class_init,
};
static void pc_machine_init_v8_1(void)
{type_register(&pc_machine_type_v8_1);
}
type_init(pc_machine_init_v8_1)

以v8.1为例,将上述代码分为四段,一一与上图对应起来:

  • pc_machine_type_v8_1
static const TypeInfo pc_machine_type_v8_1 = {.name       = "pc-i440fx-8.1" TYPE_MACHINE_SUFFIX,.parent     = TYPE_PC_MACHINE,.class_init = pc_machine_v8_1_class_init,
};

对应

  • pc_machine_v8_1_class_init
static void pc_machine_v8_1_class_init(ObjectClass *oc, void *data)
{MachineClass *mc = MACHINE_CLASS(oc);pc_i440fx_8_1_machine_options(mc);mc->init = pc_init_v8_1;
}

对应

  • pc_init_v8_1
static void pc_init_v8_1(MachineState *machine)
{void (*compat)(MachineState *m) = (NULL);if (compat) {compat(machine);}pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \TYPE_I440FX_PCI_DEVICE);
}

对应

  • pc_machine_init_v8_1
static void pc_machine_init_v8_1(void)
{type_register(&pc_machine_type_v8_1);
}
type_init(pc_machine_init_v8_1)

唯独这个pc_machine_init_v8_1图中没有明显对应对象,但根据type_init这个关键字,应对应图中这一部分:

再来回顾一下type_init,定义一个QEMU模块会调用type_init。type_init是一个宏,其定义在include/qemu/module.h中,如下:

#define type_init(function) module_init(function, MODULE_INIT_QOM)

因此,这里的

type_init(pc_machine_init_v8_1)

实际上是

static void __attribute__((constructor)) do_qemu_init_ ## function(void)    \
{                                                                           \register_module_init(function, type);                                   \
}
module_init(pc_machine_init_v8_1, MODULE_INIT_QOM)

从代码中的定义就可以看出,type_init后边的参数是一个函数(function),调用type_init就相当于调用了module_init,而在这里的函数就是pc_machine_init_v8_1,类型就是MODULE_INIT_QOM。

module_init也是一个宏,其定义也在include/qemu/module.h中,如下:

/* 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);                                   \
}

代入此处的实际值,为

static void __attribute__((constructor)) do_qemu_init_pc_machine_init_v8_1(void)
{register_module_init(pc_machine_init_v8_1, MODULE_INIT_QOM);
}

由以上代码可知,module_init最终调用了register_module_init函数。register_module_init函数在util/module.c中,代码如下:

static void init_lists(void)
{static int inited;int i;if (inited) {return;}for (i = 0; i < MODULE_INIT_MAX; i++) {QTAILQ_INIT(&init_type_list[i]);}QTAILQ_INIT(&dso_init_list);inited = 1;
}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;l = find_type(type);QTAILQ_INSERT_TAIL(l, e, node);
}

module_init_type的定义在include/qemu/module.h中,如下:

typedef enum {MODULE_INIT_MIGRATION,MODULE_INIT_BLOCK,MODULE_INIT_OPTS,MODULE_INIT_QOM,MODULE_INIT_TRACE,MODULE_INIT_XEN_BACKEND,MODULE_INIT_LIBQOS,MODULE_INIT_FUZZ_TARGET,MODULE_INIT_MAX
} module_init_type;

属于MODULE_INIT_QOM这种类型的,有一个Module列表ModuleTypeList(就是上边代码中的init_type_list,其类型为ModuleTypeList),列表中是一项一项的ModuleEntry。register_module_init函数中会设置(初始化)每一项的init函数为函数参数中的fn(第1个参数)。此处的module的init函数就是pc_machine_init_v8_1。

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;l = find_type(type);QTAILQ_INSERT_TAIL(l, e, node);
}

当然,MODULE_INIT_QOM这种类型会有很多很多的module,所有调用type_init的地方都会注册一个MODULE_INIT_QOM类型的Module。

type_init也可以说register_module_init函数只是完成了注册,也即设置了该module的init函数指针所指向的回调函数,真正调用此回调函数的地方是在module_call_init函数中,该函数也在util/module.c中,代码如下:

void module_call_init(module_init_type type)
{ModuleTypeList *l;ModuleEntry *e;if (modules_init_done[type]) {return;}l = find_type(type);QTAILQ_FOREACH(e, l, node) {e->init();}modules_init_done[type] = true;
}

在module_call_init函数中,会找到MODULE_INIT_QOM这种类型所对应的ModuleTypeList。

static ModuleTypeList *find_type(module_init_type type)
{init_lists();return &init_type_list[type];
}

而后找出(该)列表中所有的ModuleEntry,然后调用每个ModuleEntry的init函数。对应的就是module_call_init函数中的这一代码片段:

    QTAILQ_FOREACH(e, l, node) {e->init();}

这里需要注意的是,在module_call_init函数调用的这一步,所有Module的init函数都已经被调用过了。也就是说,后文书会看到很多的Module,当看到它们的时候需要意识到,其init函数在此处已被调用过了。

对于这里的

type_init(pc_machine_init_v8_1)

module_call_init函数中的

        e->init();

实际上调用的就是register_module_init函数中设置的

    e->init = fn;

也就是pc_machine_init_v8_1函数。

static void pc_machine_init_v8_1(void)
{type_register(&pc_machine_type_v8_1);
}
type_init(pc_machine_init_v8_1)

对于type_register函数的详细解析及这条主线的梳理,放在“下半场”即下一回中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652107.shtml

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

相关文章

go语言(二十一)---- channel的关闭

channel不像文件一样需要经常去关闭&#xff0c;只有当你确实没有任何发送数据了&#xff0c;或者你想显示的结束range循环之类的&#xff0c;才去关闭channel。关闭channel后&#xff0c;无法向channel再发送数据&#xff0c;&#xff08;引发pannic错误后&#xff0c;导致接收…

Linux编译实时内核和打补丁

目录 1.Linux内核2.实时内核3.编译实时内核3.1 准备3.2 获取内核源码3.3 编译3.4 设置GRUB确保启动到实时内核 4.给内核打补丁5.安装新的内核 1.Linux内核 https://github.com/torvalds/linux Linux内核是Linux操作系统的核心部分&#xff0c;它是操作系统的基本组成部分&…

spring整合mybatis的底层原理

spring整合mybatis的底层原理 原理&#xff1a; FactoryBean的自定义对象jdk动态代理Mapper接口对象 一、手写一个spring集成mybatis 目录结构&#xff1a; 1.1 入口类 public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext co…

Linux中的软链接与硬链接

Linux链接概念 Linux链接分两种&#xff0c;一种被称为硬链接&#xff08;Hard Link&#xff09;&#xff0c;另一种被称为符号链接&#xff08;Symbolic Link&#xff09;。默认情况下&#xff0c;使用 ln 命令不加参数创建硬链接&#xff0c;加 -s 参数则创建软链接 硬链接…

【mongoDB】创建用户账号和权限

使用use database_name 命令创建或切换到一个数据库 查看用户 show users 输入该命令后&#xff0c;无数据表示该数据库没有用户 创建用户 user:" freedom " 表示用户名为freedom pwd:" 123456 ” 表示密码为123456 roles:[" root "] …

一键去除图片背景——background-removal-js

一些JavaScript库和工具可以帮助实现背景去除&#xff1a; OpenCV.js&#xff1a;OpenCV的JavaScript版本&#xff0c;提供了许多计算机视觉功能&#xff0c;包括背景去除。Jimp&#xff1a;一个用于处理图像的JavaScript库&#xff0c;提供了许多图像处理功能&#xff0c;包括…

Deepin基本环境查看(六)【计算机、回收站、控制面板、文档、设备管理器】

Deepin基本环境查看 - 目录Deepin基本环境查看&#xff08;一&#xff09;【基本信息】Deepin基本环境查看&#xff08;二&#xff09;【内存】Deepin基本环境查看&#xff08;三&#xff09;【网络信息】Deepin基本环境查看&#xff08;四&#xff09;【硬盘/分区、文件系统、…

鸿蒙ArkUI 宫格+列表+HttpAPI实现

鸿蒙ArkUI学习实现一个轮播图、一个九宫格、一个图文列表。然后请求第三方HTTPAPI加载数据&#xff0c;使用了axios鸿蒙扩展库来实现第三方API数据加载并动态显示数据。 import {navigateTo } from ../common/Pageimport axios, {AxiosResponse } from ohos/axiosinterface IDa…

第5章 (python深度学习——波斯美女)

第5章 深度学习用于计算机视觉 本章包括以下内容&#xff1a; 理解卷积神经网络&#xff08;convnet&#xff09; 使用数据增强来降低过拟合 使用预训练的卷积神经网络进行特征提取 微调预训练的卷积神经网络 将卷积神经网络学到的内容及其如何做出分类决策可视化 本章将…

Android 性能优化总结:包体积优化

前言 随着开发不断迭代&#xff0c;App体积越来越大&#xff0c;包大小的增大也会给我们应用带来其他的影响 比如 下载率影响 过大的包体积会影响下载转化率&#xff0c;根据Google Play Store包体积和转化率分析报告显示&#xff0c;平均每增加1M&#xff0c;转化率下降0.2%左…

基于Java SSM框架实现大学生综合素质评分平台项目【项目源码+论文说明】

基于java的SSM框架实现大学生综合素质评分平台演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所…

C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”

安装达梦数据库后&#xff0c;使用SqlSugar连接测试数据库并基于DBFirst方式创建数据库表对应的类&#xff0c;主要代码如下&#xff1a; SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){DbType DbType.Dm,ConnectionString "Serverlocalhost; User Id…

qemu单步调试arm64 linux kernel

一、背景和目的 qemu搭建arm64 linux kernel环境-CSDN博客 之前介绍了qemu启动kernel的配置步骤和方法&#xff0c;现在开始我们的调试&#xff0c;这篇文章主要讲解如何单步调试内核&#xff0c;所有的实验还是基于ARM64&#xff1b; 二、环境准备 需要准备hostx86 target…

容器化部署 Jenkins,并配置SSH远程操作服务器

目录 一、Jenkins是什么 二、常见的部署Jenkins的方法 三、为什么选择容器化部署 四、容器化部署Jenkins步骤 1、安装 Docker 2、获取 Jenkins 镜像 3、创建并运行容器 4、访问 Jenkins 4.1 查看初始密码问题 5、配置 Jenkins 5.1 安装插件 5.2 创建管理员用户 5.3…

Java项目实战--瑞吉外卖DAY03

目录 P22新增员工_编写全局异常处理器 P23新增员工_完善全局异常处理器并测试 p24新增员工_小结 P27员工分页查询_代码开发1 P28员工分页查询_代码开发2 P22新增员工_编写全局异常处理器 在COMMON新增全局异常捕获的类&#xff0c;其实就是代理我们这些controlle。通过aop把…

基于SSM+Shiro+Druid实现的企业资产后台管理系统

系统介绍 系统演示 关注【全栈小白】视频号查看演示视频 随着企业的发展&#xff0c;很多中小企业的规模越来越大&#xff0c;需要管理资产也越来越多&#xff0c;比如显示器&#xff0c;主机&#xff0c;打印机&#xff0c;传真机&#xff0c;复印机&#xff0c;电话&#…

SAP-PP: BOM选择标识不存在

在MM03 中选择生产版本无法打开 原因是未维护 BOM选择标识 配置路径&#xff1a;生产→物料需求计划→工厂参数→执行工厂参数总体维护→BOM/工艺路线选择 T-CD: OPPQ

flutter极光推送配置厂商通道(华为)笔记--进行中

一、基础集成按照下面官方文档进行 厂商通道相关参数申请教程 集成厂商 集成指南 官方文档&#xff1a;厂商通道回执配置指南 注意&#xff1a;不同厂商对app上架的要求不同&#xff0c;华为、荣耀 对app上架没有硬性要求 遇到问题 1、引入apply plugin: com.huawei.agconn…

Jenkins部署及应用

一. 简介 二. 下载地址源&#xff1a; …

Docker部署Stable-Diffusion-webui

前排提示&#xff1a;如果不想折腾&#xff0c;可直接跳到最后获取封装好的容器&#xff0c;一键运行 :D 前言 乘上AI生成的快车&#xff0c;一同看看沿途的风景。 启一个miniconda容器 docker run -itd -v 宿主机内SD项目路径:/tmp --gpus all --ipc host -p 7860:7860 con…