【android bluetooth 框架分析 02】【Module详解 2】【gd_shim_module 模块介绍】
1. 背景
上一章节 我们介绍了 module_t 的 大体框架 ,本节内容我们就选择 我们的 gd_shim_module 模块为例子,具体剖析一下,它里面的逻辑。
static const char GD_SHIM_MODULE[] = "gd_shim_module";// system/main/shim/shim.cc
EXPORT_SYMBOL extern const module_t gd_shim_module = {.name = GD_SHIM_MODULE,.init = kUnusedModuleApi,.start_up = ShimModuleStartUp,.shut_down = GeneralShutDown,.clean_up = kUnusedModuleApi,.dependencies = {kUnusedModuleDependencies}};
-
从上述代码可以知道, gd_shim_module 模块只有 start_up 和 shut_down 两个阶段。而且他不依赖任何模块。
-
gd_shim_module 的作用:
作为 新旧蓝牙协议栈的兼容层(Shim Layer)。- 将传统的 Bluedroid 接口适配到新的 GD(Google Direct)架构。
-
设计原因:
渐进式重构蓝牙协议栈时,确保旧代码(如 Android 传统蓝牙服务)能无缝调用新模块。
生命周期:
- 在 event_start_up_stack 阶段 调用 start_up , 实际调用 ShimModuleStartUp
- 在 event_shut_down_stack 阶段 调用 shut_down, 实际调用 GeneralShutDown
那接下来我们就一起分析一下 他的 start_up 和 shut_down 中分别做了那些事情。
// system/main/shim/shim.cc
future_t* ShimModuleStartUp() {bluetooth::shim::Stack::GetInstance()->StartEverything();return kReturnImmediate;
}
// system/main/shim/stack.cc
Stack* Stack::GetInstance() {static Stack instance;return &instance;
}
-
从 Stack::GetInstance 函数中,我们不难发现, 他此时获取的 是一个 局部静态 变量 Stack 对象。 类似是一种单例的实现方式。
-
无论谁先调用 bluetooth::shim::Stack::GetInstance() 都将拿到 同一个 Stack 对象 。
-
ShimModuleStartUp() 函数其实调用的 Stack 对象的 StartEverything() 方法。
既然这里提到了 bluetooth::shim::Stack ,那我们不妨先来看一下他的数据结构。
2. bluetooth::shim::Stack
// The shim layer implementation on the Gd stack side.
namespace bluetooth {
namespace shim {// GD shim stack, having modes corresponding to legacy stack
class Stack {public:static Stack* GetInstance();Stack() = default;Stack(const Stack&) = delete;Stack& operator=(const Stack&) = delete;~Stack() = default;// Idle mode, config is loaded, but controller is not enabledvoid StartIdleMode();// Running mode, everything is upvoid StartEverything();void Stop();bool IsRunning();bool IsDumpsysModuleStarted() const;StackManager* GetStackManager();const StackManager* GetStackManager() const;legacy::Acl* GetAcl();LinkPolicyInterface* LinkPolicy();Btm* GetBtm();os::Handler* GetHandler();::rust::Box<rust::Hci>* GetRustHci() { return rust_hci_; }::rust::Box<rust::Controller>* GetRustController() {return rust_controller_;}private:mutable std::recursive_mutex mutex_;StackManager stack_manager_; // 管理 GD 协议栈中所有蓝牙模块的生命周期bool is_running_ = false;os::Thread* stack_thread_ = nullptr; // 为整个蓝牙协议栈提供专用的执行线程os::Handler* stack_handler_ = nullptr; // 提供消息处理机制,用于线程间通信和任务调度legacy::Acl* acl_ = nullptr;Btm* btm_ = nullptr;::rust::Box<rust::Stack>* rust_stack_ = nullptr;::rust::Box<rust::Hci>* rust_hci_ = nullptr;::rust::Box<rust::Controller>* rust_controller_ = nullptr;void Start(ModuleList* modules);
};} // namespace shim
} // namespace bluetooth
Stack
类是 蓝牙协议栈中的核心管理类,负责实现新的 GD (Google Direct) 蓝牙协议栈。它充当传统蓝牙协议栈和新模块化 GD 架构之间的桥梁。
1. 关键成员讲解
1. stack_manager_
StackManager stack_manager_;
-
作用:管理 GD 协议栈中所有蓝牙模块的生命周期
-
职责:
- 以正确的顺序启动和关闭模块
- 提供对模块实例的访问
- 维护模块间的依赖关系
-
与其他组件的关系:
Stack
类拥有并控制StackManager
,用它来初始化和管理所有蓝牙模块
2. stack_thread_
(os::Thread)
-
作用:为整个蓝牙协议栈提供专用的执行线程
-
特点:
- 被设置为实时优先级(REAL_TIME)
- 命名为"gd_stack_thread"
-
与其他组件的关系:
- 由
Stack
类创建和管理 - 为
stack_handler_
提供运行环境 - 被
StackManager
用来调度模块任务
- 由
3. stack_handler_
(os::Handler)
-
作用:提供消息处理机制,用于线程间通信和任务调度
-
特点:
- 与
stack_thread_
关联 - 用于处理异步操作和事件
- 与
-
与其他组件的关系:
- 由
Stack
类创建并与stack_thread_
绑定 - 被多个模块(如 ACL)用来发送和接收消息
- 由
4. ModuleList*
-
作用:包含需要启动的蓝牙模块集合
-
特点:
- 根据不同的启动模式(Idle/Everything)包含不同的模块
- 使用 add<>() 方法动态添加模块
-
与其他组件的关系:
- 被传递给
StackManager
的 StartUp 方法 - 决定了哪些模块会被初始化和运行
- 被传递给
2. Stack 类的主要功能
-
协议栈生命周期管理:
StartIdleMode()
- 启动最小配置(仅基础模块)StartEverything()
- 启动完整协议栈功能Stop()
- 关闭协议栈
-
资源管理:
- 创建和管理 PID 文件
- 确保资源的正确初始化和释放
-
模块访问接口:
- 提供获取各种模块实例的方法(如 GetAcl(), GetBtm())
-
兼容层支持:
- 维护与传统协议栈的兼容接口
3.这么设计的好处
-
模块化设计:
- 将蓝牙功能分解为独立模块,便于维护和扩展
- 允许按需加载模块,减少资源占用
-
线程安全:
- 使用递归互斥锁(mutex_)保护共享资源
- 确保多线程环境下的安全访问
-
灵活配置:
- 通过
ModuleList
支持不同配置(如仅核心功能或完整功能) - 根据系统标志(gd_rust_is_enabled 等)动态调整行为
- 通过
-
平滑过渡:
- 提供与传统协议栈兼容的接口
- 支持新旧实现共存
-
资源控制:
- 集中管理线程和消息处理程序
- 确保资源正确初始化和释放
这种设计使得蓝牙协议栈更加灵活、可维护,并且能够平滑地从传统实现过渡到新的 GD 架构,同时保持对现有应用的兼容性。
3. ShimModuleStartUp
上面我已经 为大家介绍了 Stack 数据结构, 或许你还有对 Stack 有其他疑问, 但不要着急, 我们一起来继续分析 我们 gd_shim_module 模块的 start_up 阶段。在这个过程中我们来不断体会 Stack 数据结构设计的巧妙之处。
// system/main/shim/shim.cc
future_t* ShimModuleStartUp() {bluetooth::shim::Stack::GetInstance()->StartEverything();return kReturnImmediate;
}
// system/main/shim/stack.cc
Stack* Stack::GetInstance() {static Stack instance;return &instance;
}
-
从 Stack::GetInstance 函数中,我们不难发现, 他此时获取的 是一个 局部静态 变量 Stack 对象。 类似是一种单例的实现方式。
-
无论谁先调用 bluetooth::shim::Stack::GetInstance() 都将拿到 同一个 Stack 对象 。
-
ShimModuleStartUp() 函数其实调用的 Stack 对象的 StartEverything() 方法。
3.1 Stack::StartEverything
void Stack::StartEverything() {// 1. **Rust路径**:当`gd_rust_is_enabled()`标志为真时,使用Rust实现的协议栈if (common::init_flags::gd_rust_is_enabled()) {if (rust_stack_ == nullptr) {rust_stack_ = new ::rust::Box<rust::Stack>(rust::stack_create());}rust::stack_start(**rust_stack_);// 获取HCI和Controller组件实例// 这些组件由Rust实现,通过FFI接口暴露给C++rust_hci_ = new ::rust::Box<rust::Hci>(rust::get_hci(**rust_stack_));rust_controller_ =new ::rust::Box<rust::Controller>(rust::get_controller(**rust_stack_));bluetooth::shim::hci_on_reset_complete();// Create pid since we're up and runningCreatePidFile();// Create the acl shim layeracl_ = new legacy::Acl(stack_handler_, legacy::GetAclInterface(),controller_get_interface()->get_ble_acceptlist_size(),controller_get_interface()->get_ble_resolving_list_max_size());return;}// 传统C++ 实现的 GD协议栈, 我们以 C++ 实现的分析为主// 使用递归锁保护整个启动过程// 防止多线程并发访问导致状态不一致std::lock_guard<std::recursive_mutex> lock(mutex_);ASSERT_LOG(!is_running_, "%s Gd stack already running", __func__);LOG_INFO("%s Starting Gd stack", __func__);// 方法通过`ModuleList`动态添加各种蓝牙模块:ModuleList modules;modules.add<metrics::CounterMetrics>();modules.add<hal::HciHal>();modules.add<hci::HciLayer>();modules.add<storage::StorageModule>();modules.add<shim::Dumpsys>();modules.add<hci::VendorSpecificEventManager>();modules.add<hci::Controller>();modules.add<hci::AclManager>();// 添加L2CAP相关模块if (common::init_flags::gd_l2cap_is_enabled()) {modules.add<l2cap::classic::L2capClassicModule>();modules.add<l2cap::le::L2capLeModule>();modules.add<hci::LeAdvertisingManager>();}// 添加安全模块if (common::init_flags::gd_security_is_enabled()) {modules.add<security::SecurityModule>();}modules.add<hci::LeAdvertisingManager>();modules.add<hci::LeScanningManager>();// 添加活动追踪模块if (common::init_flags::btaa_hci_is_enabled()) {modules.add<activity_attribution::ActivityAttribution>();}// 添加核心功能模块if (common::init_flags::gd_core_is_enabled()) {modules.add<att::AttModule>();modules.add<neighbor::ConnectabilityModule>();modules.add<neighbor::DiscoverabilityModule>();modules.add<neighbor::InquiryModule>();modules.add<neighbor::NameModule>();modules.add<neighbor::NameDbModule>();modules.add<neighbor::PageModule>();modules.add<neighbor::ScanModule>();modules.add<storage::StorageModule>();}// 根据上述的模块, 来实际启动协议栈Start(&modules);is_running_ = true; // 设置运行标志// 验证关键模块是否成功启动// Make sure the leaf modules are startedASSERT(stack_manager_.GetInstance<storage::StorageModule>() != nullptr);ASSERT(stack_manager_.GetInstance<shim::Dumpsys>() != nullptr);if (common::init_flags::gd_core_is_enabled()) {btm_ = new Btm(stack_handler_,stack_manager_.GetInstance<neighbor::InquiryModule>());}// 为传统API提供兼容层支持// 确保新旧实现可以协同工作if (!common::init_flags::gd_core_is_enabled()) {if (stack_manager_.IsStarted<hci::Controller>()) {acl_ = new legacy::Acl(stack_handler_, legacy::GetAclInterface(),controller_get_interface()->get_ble_acceptlist_size(),controller_get_interface()->get_ble_resolving_list_max_size());} else {LOG_ERROR("Unable to create shim ACL layer as Controller has not started");}}if (!common::init_flags::gd_core_is_enabled()) {bluetooth::shim::hci_on_reset_complete();}bluetooth::shim::init_advertising_manager();bluetooth::shim::init_scanning_manager();if (common::init_flags::gd_l2cap_is_enabled() &&!common::init_flags::gd_core_is_enabled()) {L2CA_UseLegacySecurityModule();}if (common::init_flags::btaa_hci_is_enabled()) {bluetooth::shim::init_activity_attribution();}// Create pid since we're up and runningCreatePidFile();
}
- StartEverything 我们主要分析 C++ 的流程。
主要完成如下功能:
- 向 modules 中添加 需要的模块:例如 hal::HciHal 模块和 hci::HciLayer
- 调用 Start(&modules) 来启动这些模块
- 初始化兼容层组件
- 创建PID文件标记启动完成
这种方法设计既支持新特性的逐步引入,又保持了与传统实现的兼容性,是大型系统渐进式重构的典型范例。
设计特点分析:
-
条件编译支持:
- 通过功能标志控制代码路径和模块加载
- 实现灵活的功能组合
-
模块化设计:
- 每个功能作为独立模块添加
- 模块间通过定义良好的接口交互
-
渐进式迁移:
- 支持Rust和C++实现并存
- 兼容层确保平稳过渡
-
生命周期管理:
- 明确的启动顺序控制
- 关键资源的状态验证
-
诊断支持:
- PID文件记录运行状态
- 丰富的日志输出
3.2 Stack::Start
我们来看一下 是如何将加入 modules 中的模块,启动起来的。
void Stack::Start(ModuleList* modules) {ASSERT_LOG(!is_running_, "%s Gd stack already running", __func__);LOG_INFO("%s Starting Gd stack", __func__);stack_thread_ =new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME);stack_manager_.StartUp(modules, stack_thread_);stack_handler_ = new os::Handler(stack_thread_);LOG_INFO("%s Successfully toggled Gd stack", __func__);
}
- 首先这里创建了 gd_stack_thread 线程,
- 将 我们的 modules 信息 和 stack_thread_ 线程全部传递给了 stack_manager_, 从这里就能看出来, 我们前面添加的 模块,全部由 StackManager 管理, 而且这些模块,全部都是工作在 gd_stack_thread线程中的。
- 将我们 gd_stack_thread 对应的 handler 保存在 stack_handler_ 中。
我们来继续分析 stack_manager_.StartUp(modules, stack_thread_);
3.3 StackManager::StartUp
- system/gd/stack_manager.cc
void StackManager::StartUp(ModuleList* modules, Thread* stack_thread) {// 这里有创建了一个线程 management_threadmanagement_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);// 获取到 management_thread 的 handlerhandler_ = new Handler(management_thread_);WakelockManager::Get().Acquire();std::promise<void> promise;auto future = promise.get_future();// 将 modules 的初始化全部 交给 management_thread 线程 来处理, handler_->Post(common::BindOnce(&StackManager::handle_start_up, common::Unretained(this), modules, stack_thread,std::move(promise)));// 然后 bt_stack_manager_thread 线程在这里等待 4 s, 让所有加入的模块,都初始化完成。LOG_INFO("init wait 4s.");auto init_status = future.wait_for(std::chrono::seconds(4));WakelockManager::Get().Release();// 如果 4s 后, init_status == std::future_status::ready 表明,所有的模块都已经初始化完成。 ASSERT_LOG(init_status == std::future_status::ready,"Can't start stack, last instance: %s",registry_.last_instance_.c_str());LOG_INFO("init complete");
}
- 我们 modules 的初始化 全部交给 management_thread 的 StackManager::handle_start_up 函数来处理了, 我们继续分析。
3.4 StackManager::handle_start_up
// system/gd/stack_manager.cc
void StackManager::handle_start_up(ModuleList* modules, Thread* stack_thread, std::promise<void> promise) {// 最终给到了 registry_.Start 来处理registry_.Start(modules, stack_thread);promise.set_value();
}
3.5 ModuleRegistry::Start
- system/gd/module.cc
void ModuleRegistry::Start(ModuleList* modules, Thread* thread) {for (auto it = modules->list_.begin(); it != modules->list_.end(); it++) {Start(*it, thread); // 从 modules 拿出每一个 module, 调用 Start}
}
Module* ModuleRegistry::Start(const ModuleFactory* module, Thread* thread) {// 从 started_modules_ 中查找, 当前的 module 是否已经启动,如何启动,将不再重复启动auto started_instance = started_modules_.find(module);if (started_instance != started_modules_.end()) {return started_instance->second;}// 创建一个 module 实体Module* instance = module->ctor_();LOG_INFO("Starting of %s", instance->ToString().c_str());last_instance_ = "starting " + instance->ToString();// 将 当前 module 实体和 gd_stack_thread 线程绑定set_registry_and_handler(instance, thread);LOG_INFO("Starting dependencies of %s", instance->ToString().c_str());auto start_time = std::chrono::steady_clock::now();// 如果当前启动的 module 内部还依赖 其他很多module 将,他们都加入到 实体自己的 dependencies_ 依赖中,instance->ListDependencies(&instance->dependencies_);// 先启动 当前module 所依赖的 module. 同时将 gd_stack_thread 传入Start(&instance->dependencies_, thread);LOG_INFO("Finished starting dependencies and calling Start() of %s", instance->ToString().c_str());auto end_time = std::chrono::steady_clock::now();auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();LOG_INFO("handle_start_up: Module initialization took %lld ms", elapsed_time);// 当 前module 所依赖的所有 module 都启动完毕后, 在调用自己的 Start() 函数来启动自己。instance->Start();start_order_.push_back(module);// 将启动的 module 实体加入到 started_modules_ 中, 为了避免重启启动。started_modules_[module] = instance;LOG_INFO("Started %s", instance->ToString().c_str());return instance;
}
- 下面是 logcat 中的日志截取, 可以看到我们实际加载了这么多模块。
Starting of BluetoothCounterMetrics
Starting of Storage Module
Starting of BluetoothCounterMetrics
Starting of HciHalHidl
Starting of SnoopLogger
Starting of Btaa Module
Starting of Hci Layer
Starting of Storage Module
Starting of shim::Dumpsys
Starting of Vendor Specific Event Manager
Starting of Controller
Starting of Acl Manager
Starting of Le Advertising Manager
Starting of Le Scanning Manager
我们暂时先分析到这里, 会在 后面的文章中, 分别拿 HciHalHidl 和 Hci Layer 这两个模块 具体分析。我们先梳理启动一个 module 都要执行那几步:
-
创建module 实体
- Module* instance = module->ctor_();
-
将 当前 module 实体和 gd_stack_thread 线程绑定
- set_registry_and_handler(instance, thread);
-
启动当前模块所依赖的所有子模块。
- instance->ListDependencies(&instance->dependencies_);
- Start(&instance->dependencies_, thread);
-
最后调用自己的 Start() 函数
- instance->Start();
-
将module 实体加入到 started_modules_
- started_modules_[module] = instance;
这里我们俩重点关注一下, 每一个 module 实体如何 和 gd_stack_thread 线程绑定的。
void ModuleRegistry::set_registry_and_handler(Module* instance, Thread* thread) const {instance->registry_ = this;instance->handler_ = new Handler(thread);
}
- 将我们的 gd_stack_thread 对应的 handle 直接保存在 Module->handler_ 中。
4. GeneralShutDown
// system/main/shim/shim.cc
future_t* GeneralShutDown() {bluetooth::shim::Stack::GetInstance()->Stop();return kReturnImmediate;
}
void Stack::Stop() {// First remove pid file so clients no stack is going downRemovePidFile();if (common::init_flags::gd_rust_is_enabled()) {if (rust_stack_ != nullptr) {rust::stack_stop(**rust_stack_);}return;}std::lock_guard<std::recursive_mutex> lock(mutex_);if (!common::init_flags::gd_core_is_enabled()) {bluetooth::shim::hci_on_shutting_down();}// Make sure gd acl flag is enabled and we started it upif (acl_ != nullptr) {acl_->FinalShutdown();delete acl_;acl_ = nullptr;}ASSERT_LOG(is_running_, "%s Gd stack not running", __func__);is_running_ = false;delete btm_;btm_ = nullptr;stack_handler_->Clear();stack_manager_.ShutDown();delete stack_handler_;stack_handler_ = nullptr;stack_thread_->Stop();delete stack_thread_;stack_thread_ = nullptr;LOG_INFO("%s Successfully shut down Gd stack", __func__);
}
这里看到 gd_shim_module 在结束的时候, 也是一样直接调用了 Stack::Stop , 这里不再继续展开分析了。
相关文章:
【android bluetooth 框架分析 02】【Module详解 2】【gd_shim_module 模块介绍】
1. 背景 上一章节 我们介绍了 module_t 的 大体框架 ,本节内容我们就选择 我们的 gd_shim_module 模块为例子,具体剖析一下,它里面的逻辑。 static const char GD_SHIM_MODULE[] "gd_shim_module";// system/main/shim/shim.cc …...
dbt:新一代数据转换工具
dbt(Data Build Tool)一款专为数据分析和工程师设计的开源工具,专注于 ETL/ELT 流程的数据转换(Transform)环节,帮助用户以高效、可维护的方式将原始数据转换为适合分析的数据模型。 用户只需要编写查询&am…...
Linux-内核驱动-makemenu,make modules,make uImage,杂项
动态生成设备节点设备号...
linux 内存踩踏导致的空指针问题分析纪要
1,查看日志信息打印 我们看到日志发现发包的skb模块有NULL pointer情况,我们看代码分析skb指针不可能出现是空指针,这个时候我们怀疑可能是出现了踩内存导致的空指针情况,所以我们首先需要找到系统PANIC的条件,也就是…...
【C++】 —— 笔试刷题day_14
一、乒乓球筐 题目解析 题目输入两个字符串A和B,分别代表A和B中的乒乓球,不同的大写字母就表示不同的乒乓球; 如果判断B中的所有乒乓球在A中都有,且A中每种乒乓球的数量大于等于B中的。(简单来说就是B是A的子集&#…...
在WPS中通过JavaScript宏(JSA)调用DeepSeek官方API自动识别标题级别和目录
我们希望通过AI,能够自动识别像“一”、“(一)”、“1”、“(1)” 这类常见标题序号。做一个规则,如果存在“一”时,则“一”、“(一)”、“1”分别识别为H1、H2、H3&…...
修复 WPS 编译错误:缺少:Sub或Function 且出现两个MathType加载项
问题首次出现于2025.4.12。 同时使用了Word和WPS,在里面都使用了MathType,在Microsoft的Word中,加载项能正常加载且显示,这也是我们要的效果。 而在WPS中,却出现了两个MathType,且在启动时会弹窗报错&…...
HTTP协议
目录 1 Fiddler工具 2 HTTP协议 2.1 HTTP请求和响应格式 2.2 URL 2.3 方法method 2.3.1 GET 2.3.2 POST 2.4 请求报头Header 2.5 请求正文body 2.6 http响应状态码 2.7 响应报头header HTTP协议是应用层的协议,基于传输层的TCP协议来传输,数据…...
拓扑排序 —— 2. 力扣刷题207. 课程表
题目链接:https://leetcode.cn/problems/course-schedule/description/ 题目难度:中等 相关标签:拓扑排序 / 广度优先搜搜 BFS / 深度优先搜索 DFS 2.1 问题与分析 2.1.1 原题截图 2.1.2 题目分析 首先,理解题目后必须马上意识到…...
寻找峰值 --- 二分查找
目录 一:题目 二:算法原理 三:代码实现 一:题目 题目链接:162. 寻找峰值 - 力扣(LeetCode) 二:算法原理 三:代码实现 class Solution { public:int findPeakElemen…...
SAP系统客户可回收包材库存管理
问题:客户可回收包材库存管理 现象:回收瓶无库存管理,在库数量以及在客户的库存数量没有统计,管理混乱。 解决方法: 客户可回收包装材料在SAP有标准的解决方案,在集团尚未启用该业务,首先…...
C++标识符:检查是否和保留字冲突
1. 基础知识 最基本的要求: 字母、数字、下划线组成, 并且不能是数字开头。 禁忌1: C 关键字不能用做标识符。 它们是: alignas alignof asm auto bool break case catch char char16_t char32_t class const constexpr const_…...
【Java多线程】告别线程混乱!深度解析Java多线程4大实现方式(附实战案例)
一、继承Thread类 实现步骤: 1.继承Thread类 2.重写run()方法 3.创建线程对象并调用start()方法 示例: class MyThread extends Thread {Overridepublic void run() {for (int i 0; i < 5; i) {System.out.println(Thread.currentThread().getNam…...
Linux安装yum和python
一、安装yum(CentOS) 查看yum版本 yum --version 如果未安装,执行以下部分: 1. 确保你的系统中已经安装了epel仓库,如果没有安装可以通过以下命令安装: sudo yum install epel-release 2.yum安装 – CentOS/RHEL系统&#…...
【数据结构】HashMap源码 —— 简单介绍
HashMap源码介绍 下面并非完整的源码,主要简单了解其流程。 1. 基本成员变量 哈希桶/开散列,链地址法/开链法是由:数组 链表(单链表) 红黑树(当数组长度>64 && 链表长度>8以后,链表变成红黑树…...
149页研读——华为基于IPD全过程研发质量管理【附全文阅读】
本文介绍了IPD(集成产品开发)的全过程研发质量管理,强调了以客户需求为导向,通过跨部门协同、资源整合、快速响应等方式提高研发效率和成功率。文章详细阐述了IPD研发管理体系的精要,包括其核心思想、优势、框架以及核心理念。 其中,跨领域平台与技术研发、端到端流程与项…...
深入理解 v-for 指令及其使用方法
在 Vue.js 中,v-for 是用于渲染列表的核心指令,它允许你通过循环渲染数据源中的每一项。通过 v-for,你可以轻松地将数组、对象或其他可迭代的数据渲染成 HTML 元素。本文将详细介绍 v-for 的基本用法、常见的应用场景、最佳实践及性能优化&am…...
swift菜鸟教程24-25(可选链,自动引用计数)
一个朴实无华的目录 今日学习内容:1.Swift 可选链1.1定义1.2通过可选链调用方法1.3使用可选链调用下标脚本1.4通过可选链接调用来访问下标1.4访问可选类型的下标 2.Swift 自动引用计数(ARC)2.1实例之间的循环强引用会造成内存泄露2.2弱引用&a…...
使用 Visual Studio 2022 (VS2022) 编译 FreeCAD 1.0.0 的详细教程
一、环境准备 官方教程:在 Windows 上编译 - FreeCAD Documentation Windows 10/11(推荐) git vs2022 cmake 3.26.4 Doxygen1.12 二、获取源码与依赖 版本关系 打开Git Bash或CMD,执行以下命令 git clone --recurse-sub…...
机械臂只有位置信息是否可以进行手眼标定?
平常我在做手眼标定时,一般都是通过OpenCV的cv::calibrateHandEye函数进行求解,需要输入多组不同的机械臂位姿。今天遇到了一款舵机机器人,只能获取位置,得不到姿态信息,想着那就把姿态都设为0,结果求不出来…...
Unity入门
文章目录 Unity脚本基础大基础生命周期函数Inspector窗口显示常用特性辅助特性 MnonBehaviour基类成员变量成员方法 组件GameObject成员变量gameObject静态方法成员方法 组件Time静态成员变量 组件TransformVector3结构体基础基本概念常用向量表示常用方法 位置与位移位置posit…...
《汽车制造技术基础》第一次作业
作业内容 查阅相关资料,谈谈对汽车制造技术的发展的理解。 可以是关于汽车的先进制造技术 或 汽车先进制造技术 与 制造理念的发展趋势 或 汽车先进制造技术对环境与可持续发展的影响等。 以下从技术突破、制造理念转型及环境影响三个维度展开对汽车制造技…...
烟花爆竹储存作业安全要求
烟花爆竹储存作业证是从事相关作业的法定凭证,旨在确保操作人员具备专业知识和安全技能,防止因违规操作引发火灾、爆炸等事故。根据《烟花爆竹安全管理条例》及相关法规,未取得作业证的人员不得从事烟花爆竹储存、搬运、管理等作业。 仓库选址…...
Flask+Plotly结合动态加载图形页面实践
1. DeepSeek帮我实践 1.1. 我的提问既设计方案 原有如下主页:dashboard.html,现增加“预测模型学习”,对感知机神经网络描述如下: 1、输入与输出为固定值,例如输入层215,输出层48; 2、模型为回归神经网络; 3、中层是可动态调整的,例如定义如下:第二层,200,第三层…...
leetcode每日一题:统计好整数的数目
题目 给你两个 正 整数 n 和 k 。 如果一个整数 x 满足以下条件,那么它被称为 k 回文 整数 。 x 是一个 回文整数 。 x 能被 k 整除。 如果一个整数的数位重新排列后能得到一个 k 回文整数 ,那么我们称这个整数为 好 整数。比方说,k 2 …...
《2025蓝桥杯C++B组:D:产值调整》
**作者的个人gitee** 作者的算法讲解主页▶️ 每日一言:“泪眼问花花不语,乱红飞过秋千去🌸🌸” 题目 二.解题策略 本题比较简单,我的思路是写三个函数分别计算黄金白银铜一次新产值,通过k次循环即可获…...
【模块化拆解与多视角信息1】基础信息:隐藏的筛选规则——那些简历上没说出口的暗号
写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…...
当Browser Use遇见A2A:浏览器自动化与智能体协作的“冰与火之歌“
——一场正在改写数字文明的技术奇遇 第一章 浏览器革命:从"手动挡"到"自动驾驶" 1.1 传统自动化工具的"中年危机" 还记得2023年那个抓狂的凌晨吗?你蹲守演唱会门票时,Selenium脚本因为验证码识别失败第108次…...
Python:开启自动化办公与游戏开发的无限可能
重要的事情放在前面 Python自动化办公和游戏 Python:开启自动化办公与游戏开发的无限可能 在数字化时代的浪潮中,Python以其强大的功能和简洁的语法,成为了众多开发者手中的得力工具,尤其在自动化办公与游戏开发领域࿰…...
腾讯后台开发 一面
一、手撕 合并升序链表 合并两个排序的链表_牛客题霸_牛客网 顺时针翻转矩阵 顺时针旋转矩阵_牛客题霸_牛客网 二、八股 1、静态变量和实例变量 public class House {public static String buildDate "2024-10-27"; // 静态变量public String color; // 实…...
基于生成对抗网络(GAN)的手写数字生成实践
基于生成对抗网络(GAN)的手写数字生成实践 一、图像生成的技术演进 在人工智能领域,图像生成技术经历了从传统算法到深度学习的革命性发展。其中,生成对抗网络(Generative Adversarial Networks, GANs)作…...
网络互连与互联网
1.在路由表中找不到目标网络时使用默认路由,默认路由通常指本地网关的地址。 2.OSPF最主要的特征是使用分布式链路状态协议,而RIP使用的是距离向量协议。 3.OSPF使用链路状态公告LSA扩散路由信息 4.内部网关路由协议IGRP是一种动态距离矢量路由协议&a…...
大模型常见面试题
大模型常见面试题 大模型相关的面试问题通常涉及模型的原理、应用、优化以及面试者对于该领域的理解和经验。以下是一些常见的 大模型面试问题以及建议的回答方式: 请简述什么是大模型,以及它与传统模型的主要区别是什么? 回答:…...
python高级编程一(生成器与高级编程)
@TOC 生成器 生成器使用 通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素占 ⽤的空间都⽩⽩浪费了。所以,如果…...
Linux线程属性与多线程开发:API详解与实战代码解析
Linux 线程的属性 线程池 多线程的创建 线程的属性 引入 我们设想一个场景,使用pthread_detach时,发现线程早就已经结束了,这时候pthread_detach还能正常发挥清理线程的 独有空间 的作用吗? 答案是可以的,但是这难…...
Inkscape安装教程
Inkscape 是一款开源的矢量图形编辑软件,功能强大且免费,适用于 Windows、macOS 和 Linux 系统。以下是在不同操作系统上安装 Inkscape 的详细教程: 一、Windows 系统安装 Inkscape 1. 下载安装包 打开浏览器,访问 Inkscape 官方…...
危化品安全员岗位注意事项有哪些?
危化品安全员肩负着保障危化品生产、储存、运输和使用等环节安全的重要职责,其岗位注意事项涉及多个方面,以下是一些主要内容: 法规标准与制度执行 必须熟悉并严格遵守国家和地方有关危化品安全管理的法律法规、标准规范,如《危险…...
1、从零搭建魔法工坊:React 19 新手村生存指南
一、开篇:新世界的入场券 "你好,年轻的魔法学徒!欢迎来到React魔法世界。我是你的向导赫敏韦斯莱,今天我们将用React 19这根全新魔杖,搭建属于你的第一座魔法工坊。" ——以对话形式开场,消除技…...
链表代码实现(C++)
数据结构第三篇 一、几个注意点 1、同时持有头尾结点的引用 双链表一般同时持有头尾结点的引用 因为在工程应用中,通常在容器尾插入元素,双链表持有尾部节点的引用,就可以在O(1)时间复杂度的情况下在尾部添加元素。…...
【学习笔记】两个类之间的数据交互方式
在面向对象编程中,两个类之间的数据交互可以通过以下几种方式实现,具体选择取决于需求和设计模式: 1. 通过方法调用 一个类通过调用另一个类的公共方法来获取或传递数据。这是最常见的方式,符合封装原则。 class ClassA:def __…...
【Docker基础】深入解析 Docker 存储卷:管理、绑定与实战应用
文章目录 一、什么是存储卷二、为什么需要存储卷三、存储卷分类四、管理卷 Volume方式一:Volume 命令操作方式二:使用 -v 或 --mount 参数指定卷方式三:Dockerfile 匿名卷 五、操作案例Docker 命令创建管理卷Docker -v 创建管理卷Docker 卷生…...
Python生成exe
其中的 -w 参数是 PyInstaller 用于窗口模式(Windowed mode),它会关闭命令行窗口的输出,这通常用于 图形界面程序(GUI),比如使用 PyQt6, Tkinter, PySide6 等。 所以: 如果你在没有…...
SpringBoot原理
配置优先级 SpringBoot项目当中支持的三类配置文件: 在SpringBoot项目当中,我们要想配置一个属性,可以通过这三种方式当中的任意一种来配置都可以,那么如果项目中同时存在这三种配置文件,且都配置了同一个属性&#x…...
Google 官方提示工程 (Prompt Engineering)白皮书 总结
《大语言模型的提示工程:从基础到最佳实践》 总结 本文围绕大语言模型的提示工程展开,介绍其是设计高质量提示引导 LLM 产生准确输出的过程。探讨了 LLM 输出配置如输出长度、温度、top-K 和 top-P 等设置及其相互影响,阐述了零样本、少样本…...
Python——numpy测试题目
题目: 生成一个2行3列随机整数二维数组a使用Numpy方法对(1)中数组a进行整体求积使用Numpy方法对(1)中数组a进行求每列最大值索引定义一个NumPy一维数组 b,元素为 1 到 10 的整数获取(4&#x…...
【SLAM】将realsense-viewer录制的rosbag视频导出成图片序列(RealSense D435)
本文介绍了如何将realsense-viewer录制的rosbag格式的视频导出成图片序列,方便合并成mp4视频或插入到论文中。 本文首发于❄慕雪的寒舍 说明 Intel提供的realsense-viewer软件录制的视频都是rosbag格式的,为了编写论文,需要从录制的视频中截…...
Unity6国际版下载
Unity6国际版下载下载地址 Hub下载地址:https://www.nounitycn.top/unityhub 先下载unity6启动器(下载速度很快),在去下载unity6000版本(下载速度慢) 下载速度很慢的话,有条件可以找梯子科学上网…...
2025认证杯挑战赛B题【 谣言在社交网络上的传播 】原创论文讲解(含完整python代码)
大家好呀,从发布赛题一直到现在,总算完成了认证杯数学中国数学建模网络挑战赛第一阶段B题目谣言在社交网络上的传播完整的成品论文。 给大家看一下目录吧: 目录 摘 要: 一、问题重述 二. 问题分析 2.1问题一 2.…...
后台进程管理之pstree 和 job
1. pstree 命令 功能 以树状结构显示进程间的父子关系,直观展示进程的层次结构。 常用选项 选项说明-p显示进程 PID-a显示完整命令行(包括参数)-u显示进程所属用户-n按 PID 排序(默认按进程名)-h高亮当前进程及其祖…...
波束形成(BF)从算法仿真到工程源码实现-第三节-延迟求和波束形成(DSB)
一、概述 本节我们讨论延迟求和波束形成算法,包括原理分析及代码实现。 更多资料和代码可以进入 https://t.zsxq.com/qgmoN ,同时欢迎大家提出宝贵的建议,以共同探讨学习。 二、原理分析 2.1 原理: 首先对不同麦克风信号之间的相…...