当前位置: 首页 > 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 属性

相关文章:

qemu学习笔记:QOM

2.4 QOM介绍 说明&#xff1a;小白学习qemu的一些学习笔记。主要是学习《QEMU&KVM源码解析与应用》这本书。 参考&#xff1a; 《QEMU&KVM源码解析与应用》作者&#xff1a;李强 Qemu - 百问网嵌入式Linux wiki QOM 定义&#xff1a;QEMU Object Model&#xff0c;是 Q…...

Spring AI开发跃迁指南(第二章:急速上手3——Advisor核心原理、源码讲解及使用实例)

1.Advisor简介 Spring AI 中的 Advisor 是一种核心机制&#xff0c;用于拦截和增强 AI 应用程序中的请求与响应流。其设计灵感来源于 Spring AOP&#xff08;面向切面编程&#xff09;中的切面&#xff08;Aspect&#xff09;概念&#xff0c;但专门针对 AI 交互场景进行了优化…...

51c嵌入式~单片机~合集9

我自己的原文哦~ https://blog.51cto.com/whaosoft/13884964 一、单片机中hex、bin文件的区别 单片机程序编译之后&#xff0c;除了生成hex文件之外还生成了bin文件&#xff0c;实际它们都是单片机的下载文件&#xff0c;下文介绍它们的区别。 Hex Hex文件包含地址信息。…...

linux学习——数据库API创建

一.API操作 1.int sqlite3_open(char *filename,sqlite3 **db) 功能&#xff1a;打开sqlite数据库 参数&#xff1a; filename:数据库文件路径 db:指向sqlite句柄的指针 &#xff08;splite3* db;&#xff09; 返回值…...

21.2Linux中的LCD驱动实验(驱动)_csdn

1、修改设备树 1.1、LCD 屏幕使用的 IO 配置 编译&#xff1a; make uImage LOADADDR0XC2000040 -j8 //编译内核复制给内核的镜像路径&#xff1a;1.2、LDTC 接口节点修改 1.3、输出接口的编写 2、在 panel-simple.c 文件里面添加屏幕参数 显示波浪线是因为alientek_desc 保存参…...

Dubbo(89)如何设计一个支持多语言的Dubbo服务?

设计一个支持多语言的Dubbo服务需要考虑以下几个方面&#xff1a; 服务接口设计&#xff1a;确保服务接口的定义可以被不同语言实现。序列化协议&#xff1a;选择一个支持多语言的序列化协议&#xff0c;例如Protobuf、Thrift、gRPC等。服务注册与发现&#xff1a;确保服务注册…...

油气地震资料数据中“照明”的含义

油气地震资料数据中“照明”的含义 在地震勘探中&#xff0c;“照明”&#xff08;Illumination&#xff09;是一个比喻性术语&#xff0c;用于描述地下地质构造被地震波能量覆盖的程度。其核心含义包括&#xff1a; 能量覆盖&#xff1a;指地震波&#xff08;如人工激发的地…...

[FPGA Video IP] Frame Buffer Read and Write

Xilinx Video Frame Buffer Read and Write IP (PG278) 详细介绍 概述 Xilinx LogiCORE™ IP Video Frame Buffer Read&#xff08;帧缓冲读取&#xff09;和 Video Frame Buffer Write&#xff08;帧缓冲写入&#xff09;核&#xff08;PG278&#xff09;是一对专为视频处理…...

新能源行业供应链规划及集成计划报告(95页PPT)(文末有下载方式)

资料解读&#xff1a;《数字化供应链规划及集成计划现状评估报告》 详细资料请看本解读文章的最后内容。 该报告围绕新能源行业 XX 企业供应链展开&#xff0c;全面评估其现状&#xff0c;剖析存在的问题&#xff0c;并提出改进方向和关键举措&#xff0c;旨在提升供应链竞争力…...

curl详解

curl 是一个常用的命令行工具&#xff0c;用于发送 HTTP 请求&#xff0c;支持包括 GET、POST、PUT、DELETE 等在内的多种 HTTP 方法。它非常适合用来测试 API、下载文件、与后端服务进行交互等。接下来&#xff0c;我会详细讲解 curl 的基本用法以及常见的应用场景。 &#x…...

博客打卡-人类基因序列功能问题动态规划

题目如下&#xff1a; 众所周知&#xff0c;人类基因可以被认为是由4个核苷酸组成的序列&#xff0c;它们简单的由四个字母A、C、G和T表示。生物学家一直对识别人类基因和确定其功能感兴趣&#xff0c;因为这些可以用于诊断人类疾病和设计新药物。 生物学家确定新基因序列功能…...

Runnable组件动态添加默认调用参数

01. bind 函数用途与使用技巧 在使用 LangChain 开发时&#xff0c;某些场景我们希望在一个 Runnable 队列中调用另一个 Runnable 并传递常量参数&#xff0c;这些参数既非前序 Runnable 的输出&#xff0c;也不是用户输入&#xff0c;而是组件自身的部分参数。此时可以使用 R…...

系统架构设计师:设计模式概述

面向对象技术为软件技术带来新的发展。人们运用面向对象的思想分析系统、为系统建模并设计系统&#xff0c;最后使用面向对象的程序语言来实现系统。 但是面向对象的设计并不是一件很简单的事情&#xff0c;尤其是要设计出架构良好的软件系统更不容易。 为了提高系统的复用性…...

天山流域流量数据集(1991-2019)

时间分辨率日空间分辨率/共享方式开放获取数据大小131.67 MB数据时间范围 1901-01-01 — 2019-12-31 元数据更新时间2025-03-24 数据集摘要 由于天山地区数据稀缺和水文条件复杂&#xff0c;中亚水塔的自然径流数据集在各种全球径流数据集&#xff08;如GMIS、GRDC&#xff09…...

Linux 环境下 Mysql 5.7 数据定期备份

目录 一、创建数据备份脚本二、查看备份日志三、数据库数据恢复 备份策略&#xff1a; 系统环境 openEuler 22.03 (LTS-SP4) 单机备份 每天凌晨2点&#xff0c;指定数据库表全量备份&#xff0c;只保留近7次备份数据 每次的脚本执行&#xff0c;将会记录执行结果到日志&#xf…...

多模态大语言模型arxiv论文略读(五十二)

M3D: Advancing 3D Medical Image Analysis with Multi-Modal Large Language Models ➡️ 论文标题&#xff1a;M3D: Advancing 3D Medical Image Analysis with Multi-Modal Large Language Models ➡️ 论文作者&#xff1a;Fan Bai, Yuxin Du, Tiejun Huang, Max Q. -H. M…...

REST API、FastAPI与Flask API的对比分析

以下是关于REST API、FastAPI与Flask API的对比分析&#xff0c;涵盖架构设计、性能表现、开发效率等核心维度&#xff1a; 一、核心定位与架构差异 REST API 本质&#xff1a;一种基于HTTP协议的架构风格&#xff0c;强调资源化操作&#xff08;通过URI定位资源&#xff09;、…...

【论文阅读26】贝叶斯-滑坡预测-不确定性

&#x1f4d6; 这篇论文主要说了什么&#xff1f; &#x1f4cc; 背景&#xff1a; 滑坡预测里&#xff0c;预测失稳时间&#xff08;Slope Failure Time, SFT&#xff09; 很关键&#xff0c;但它受两方面不确定性影响&#xff1a; 观测不确定性&#xff08;监测数据本身的…...

【笔记】深度学习模型训练的 GPU 内存优化之旅④:内存交换与重计算的联合优化篇

开设此专题&#xff0c;目的一是梳理文献&#xff0c;目的二是分享知识。因为笔者读研期间的研究方向是单卡上的显存优化&#xff0c;所以最初思考的专题名称是“显存突围&#xff1a;深度学习模型训练的 GPU 内存优化之旅”&#xff0c;英文缩写是 “MLSys_GPU_Memory_Opt”。…...

边缘计算革命:大模型轻量化部署全栈实战指南

当ResNet-152模型能在树莓派4B上实现每秒27帧实时推理时&#xff0c;边缘智能时代真正到来。本文解析从模型压缩到硬件加速的完整技术栈&#xff0c;实测Transformer类模型在移动端的部署时延可压缩至16ms&#xff0c;揭示ARM芯片实现INT4量化的工程秘诀与十种典型场景优化方案…...

LangChain4j +DeepSeek大模型应用开发——7 项目实战 创建硅谷小鹿

这部分我们实现硅谷小鹿的基本聊天功能&#xff0c;包含聊天记忆、聊天记忆持久化、提示词 1. 创建硅谷小鹿 创建XiaoLuAgent package com.ai.langchain4j.assistant;import dev.langchain4j.service.*; import dev.langchain4j.service.spring.AiService;import static dev…...

python自动化测试

Python自动化测试指南 Python是自动化测试领域的首选语言之一,凭借其简洁的语法、丰富的库和强大的生态系统,能够高效地实现各种测试需求。本文将详细介绍Python在自动化测试中的应用,涵盖Web测试、API测试、单元测试、GUI测试等多个方面。 1. 自动化测试基础 测试金字塔…...

49、【OS】【Nuttx】【OSTest】参数解析:测试项

背景 接之前 blog 48、【OS】【Nuttx】【OSTest】内存监控&#xff1a;分配释放推演 解析完内存监控&#xff0c;继续看下一个测试项 getopt_test 测试项 getopt_test 如下 getopt&#xff0c;getopt_long&#xff0c;getopt_long_only getopt() 用来解析命令行短选项&am…...

String StringBuilder StringBuffer

文章目录 StringStringBuilderStringBuffer StringStringBuilderStringBuffer可变性不可变可变可变线程安全安全&#xff08;天然不可变&#xff09;不安全安全&#xff08;同步方法&#xff09;性能低&#xff08;频繁操作生成新对象&#xff09;高中&#xff08;同步开销&…...

[FPGA 官方 IP] Binary Counter

Xilinx Binary Counter IP (PG121) 详细介绍 概述 Xilinx Binary Counter IP&#xff08;二进制计数器 IP&#xff09;是 AMD Xilinx 提供的 LogiCORE™ IP 核&#xff0c;用于在 FPGA 中实现高性能、面积高效的二进制计数器。该 IP 核支持上行计数器、下行计数器以及上/下计…...

【大模型实战篇】华为信创环境采用vllm部署QwQ-32B模型

1. 背景 本文分享在华为昇腾机器上部署QwQ-32B模型的实践。 首先华为自己是提供了一套在信创机器&#xff08;NPU&#xff09;上部署模型的方案【1】&#xff0c;但是部署之后&#xff0c;测试发现会有输出截断的现象。QwQ-32B本身是支持128k的最大上下文长度&#xff0c;定位…...

优雅关闭服务:深入理解 SIGINT / SIGTERM 信号处理机制

目录 为什么需要优雅关闭&#xff1f; 什么是 SIGINT 和 SIGTERM&#xff1f; 如何实现优雅关闭&#xff08;以 C 为例&#xff09; 示例代码&#xff08;gRPC 服务 Boost 信号监听&#xff09;&#xff1a; 优雅关闭时的清理内容通常包括&#xff1a; 与 SIGKILL 的区别…...

2025五一杯数学建模竞赛选题建议+初步分析

完整内容请看文章最下面的推广群 2025五一杯数学建模竞赛选题建议初步分析 提示&#xff1a;C君认为的难度和开放度评级如下&#xff1a; 难度&#xff1a;B题 > A题 > C题,开放度&#xff1a;B题 > C题 > A题。综合来看&#xff1a;A题目标明确&#xff0c;数据…...

自动剪辑批量混剪视频过原创软件工具视频帧级处理技术实践批量截图解析

一、引言&#xff1a;视频素材精细化处理的技术需求 在视频内容生产与分析场景中&#xff0c;高效的帧级处理是素材解构的核心环节。本文结合实战经验&#xff0c;解析基于智能帧截取算法、参数化配置系统、多线程并行处理的批量帧处理技术方案&#xff0c;构建可复用的工程化…...

GD32F407单片机开发入门(二十五)HC-SR04超声波模块测距实战含源码

文章目录 一.概要二.HC-SR04主要参数1.模块引脚定义2.模块电气参数3.模块通讯时序4.模块原理图 三.GD32单片机超声波模块测距实验四.工程源代码下载五.小结 一.概要 HC-SR04超声波模块常用于机器人避障、物体测距、液位检测、公共安防、停车场检测等场所。HC-SR04超声波模块主…...

C++11新特性_Lambda 表达式

Lambda 表达式是 C11 引入的一项重要特性&#xff0c;它允许你在代码中创建匿名函数对象。Lambda 表达式为编写简洁、灵活的代码提供了便利&#xff0c;尤其适用于函数式编程和需要传递简短回调函数的场景。下面从基本语法、捕获列表、使用场景等方面详细介绍 Lambda 表达式。 …...

vue中$set原理

Vue 中的 $set 方法&#xff08;Vue.set&#xff09;主要用于 向响应式对象中添加一个新的属性&#xff0c;并确保这个新属性是响应式的&#xff0c;能够触发视图更新。 &#x1f4cc; 背景问题&#xff1a;为什么需要 $set&#xff1f; 在 Vue 2 中&#xff0c;直接给对象新增…...

【C++重载操作符与转换】输入和输出操作符

目录 一、输入输出操作符概述 二、输入输出操作符重载的原理 2.1 为什么需要重载&#xff1f; 2.2 重载的限制 2.3 重载的方式 三、输入输出操作符重载的实现 3.1 输出操作符 << 的重载 3.2 输入操作符 >> 的重载 四、输入输出操作符重载的注意事项 4.1 …...

Vue 生命周期全解析:理解组件从创建到销毁的全过程

Vue 生命周期全解析&#xff1a;理解组件从创建到销毁的全过程 Vue.js 是一个流行的前端框架&#xff0c;它通过“组件化开发”提升了代码组织效率。要真正掌握 Vue&#xff0c;生命周期&#xff08;Lifecycle&#xff09; 是一个必须深入理解的核心概念。生命周期不仅决定了组…...

MySQL零基础入门:Ubuntu环境安装与操作精解

知识点1【数据库】 数据的存储方式&#xff0c;我们之前学的&#xff0c;从变量&#xff0c;数组&#xff0c;链表&#xff0c;最后到文件&#xff0c;文件之上&#xff0c;便是数据库&#xff0c;而我们要介绍的MySQL就是数据库的关系数据库中的其中一种。 1、数据库 本质&…...

【计算机视觉】语义分割:Mask2Former:统一分割框架的技术突破与实战指南

深度解析Mask2Former&#xff1a;统一分割框架的技术突破与实战指南 技术架构与创新设计核心设计理念关键技术组件 环境配置与安装指南硬件要求安装步骤预训练模型下载 实战全流程解析1. 数据准备2. 配置文件定制3. 训练流程4. 推理与可视化 核心技术深度解析1. 掩膜注意力机制…...

Qt二维码demo

使用QZXing库生成的二维码demo 运行结果 实现代码 c文件 #include "mainwindow.h" #include "ui_mainwindow.h" #include "src/myqrcodeheader.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui-&…...

Java 基础--数组(Array):存储数据的“排排坐”

作者&#xff1a;IvanCodes 发布时间&#xff1a;2025年5月1日&#x1f913; 专栏&#xff1a;Java教程 大家好&#xff01;&#x1f44b; 咱们在编程时&#xff0c;经常需要处理一批相同类型的数据&#xff0c;比如班级里所有同学的成绩 &#x1f4af;、一周每天的最高气温 …...

OpenGL-ES 学习(10) ---- OpenGL-ES Shader语言语法

目录 Shader 举例Shader 语法版本规范声明变量和定法方法向量构造方法矩阵构造方法结构&#xff0c;数组&#xff0c;函数定义结构数组函数 内建函数条件语句和运算符统一变量统一变量块Shader 输入输出插值限定符预处理命令精度限定符不变性 Shader 举例 一个典型的简单的 Sh…...

Unity SpriteAtlas (精灵图集)

&#x1f3c6; 个人愚见&#xff0c;没事写写笔记 &#x1f3c6;《博客内容》&#xff1a;Unity3D开发内容 &#x1f3c6;&#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f50e;为什么要打图集&#xff1f; &#x1f4a1;打图集的目的就是减少DrawCall 提高性能 &a…...

(33)VTK C++开发示例 ---图片转3D

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 这是 VTK 测试 clipArt.tcl 的改编版本。 提供带有 2D 剪贴画的 jpg 文件&#xff0c;该示例将创建 3D 多边形数据模…...

RAG工程-基于LangChain 实现 Advanced RAG(预检索-查询优化)(上)

Enrich 完善问题 完善问题流程概述 问题转述 在典型RAG架构中&#xff0c;用户问题的质量直接影响检索系统的表现。研究表明&#xff0c;未经优化的自然语言查询会导致&#xff1a; 关键实体识别缺失 语义漂移导致召回偏离 长尾问题检索失败率升高 大多数用户并非提示词工程…...

交我算使用保姆教程:在计算中心利用singularity容器训练深度学习模型

文章目录 准备工作步骤如何封装和使用容器安装创建 Singularity 容器编写 def 文件构建容器查看构建容器的 python 版本本地测试挂载数据集和代码 如何上传数据windows 系统Linux 系统 如何设置作业任务脚本的结构常用的 Slurm 参数一份完整的 slurm 作业示例 如何在 debug 队列…...

CMake中强制启用option定义变量的方法

在CMake中&#xff0c;若要在另一个CMake文件中强制启用由option()定义的变量&#xff0c;可使用set(... FORCE)覆盖缓存变量。具体步骤如下&#xff1a; 使用set命令强制覆盖缓存&#xff1a; 在需要强制启用选项的CMake文件中&#xff0c;使用set命令并指定CACHE和FORCE参数。…...

图解 Git 工作流:理解 Rebase、Merge 与 Pull Request 的区别

图解 Git 工作流&#xff1a;理解 Rebase、Merge 与 Pull Request 的区别 在多人协作开发中&#xff0c;选择合适的 Git 分支管理策略至关重要。Merge、Rebase 和 Pull Request 是最常见的三种方式&#xff0c;它们本质不同&#xff0c;使用场景也不同。 本文将通过流程图&am…...

图与网络模型

目录 图的基本概念 例题&#xff1a;比赛的安排 MATLAB作图 最短路径模型 Dijkstra算法步骤 最短路径的Dijkstra算法示例 Dijkstra算法的Matlab函数 最短路径的Floyd算法模型 最短路径的Floyd算法步骤 Floyd算法的Matlab函数 图的基本概念 图G是一个二重组&#xff1a; …...

连接linux虚拟机并运行C++【从0开始】

连接linux虚拟机并运行C【从0开始】 NetSarang安装后两个&#xff0c;其实更加常用的 安装VMware安装Ubuntu 的 ISO 镜像VMWare--TipsUbuntu快捷键&#xff0c;可以在设置里面修改 连接Linux运行cwhy剪不断&#xff0c;理还乱操作 因为好多判题系统&#xff0c;后台都是Linux环…...

多线程系列二:Thread类

Thread类是jvm用来管理线程的一个类&#xff0c;换句话说&#xff0c;每个线程都有一个唯一的Thread对象与之关联 1.Thread常见构造方法 Thread()&#xff1a;创建线程对象Thread(Runnable target)&#xff1a;使用Runnable对象创建线程对象Thread(String name)&#xff1a;创…...

2025五一杯数学建模C题:社交媒体平台用户分析问题,完整第一问模型与求解+代码

完整代码模型请见文末名片 • 问题1分析&#xff1a; – 来龙去脉和与其他问题的内在联系&#xff1a; • 来龙去脉&#xff1a;社交媒体平台为了评估博主的价值&#xff0c;合理分配资源和优化内容推荐&#xff0c;需要准确预测博主的新增关注数。新增关注数是衡量博主影响…...

开源飞控软件:推动无人机技术进步的引擎

在过去的二十年里&#xff0c;众多开源自动驾驶仪项目极大地推动了无人机技术的发展。像 MatrixPilot、Baseflight、TauLabs、OpenPilot、Cleanflight、MultiWii 和 dRonin 等一些开源自动驾驶仪项目已经停止开发&#xff0c;然而&#xff0c;Ardupilot/APM、Pixhawk/PX4、Papa…...