【android bluetooth 框架分析 02】【Module详解 2】【gd_shim_module 模块介绍】

news2025/4/15 15:56:21

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 enabled
  void StartIdleMode();
  // Running mode, everything is up
  void 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 类的主要功能

  1. 协议栈生命周期管理

    • StartIdleMode() - 启动最小配置(仅基础模块)
    • StartEverything() - 启动完整协议栈功能
    • Stop() - 关闭协议栈
  2. 资源管理

    • 创建和管理 PID 文件
    • 确保资源的正确初始化和释放
  3. 模块访问接口

    • 提供获取各种模块实例的方法(如 GetAcl(), GetBtm())
  4. 兼容层支持

    • 维护与传统协议栈的兼容接口

3.这么设计的好处

  1. 模块化设计

    • 将蓝牙功能分解为独立模块,便于维护和扩展
    • 允许按需加载模块,减少资源占用
  2. 线程安全

    • 使用递归互斥锁(mutex_)保护共享资源
    • 确保多线程环境下的安全访问
  3. 灵活配置

    • 通过 ModuleList 支持不同配置(如仅核心功能或完整功能)
    • 根据系统标志(gd_rust_is_enabled 等)动态调整行为
  4. 平滑过渡

    • 提供与传统协议栈兼容的接口
    • 支持新旧实现共存
  5. 资源控制

    • 集中管理线程和消息处理程序
    • 确保资源正确初始化和释放

这种设计使得蓝牙协议栈更加灵活、可维护,并且能够平滑地从传统实现过渡到新的 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 running
    CreatePidFile();

    // Create the acl shim layer
    acl_ = 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 started
  ASSERT(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 running
  CreatePidFile();
}
  • StartEverything 我们主要分析 C++ 的流程。

主要完成如下功能:

  1. 向 modules 中添加 需要的模块:例如 hal::HciHal 模块和 hci::HciLayer
  2. 调用 Start(&modules) 来启动这些模块
  3. 初始化兼容层组件
  4. 创建PID文件标记启动完成

这种方法设计既支持新特性的逐步引入,又保持了与传统实现的兼容性,是大型系统渐进式重构的典型范例。

设计特点分析:

  1. 条件编译支持

    • 通过功能标志控制代码路径和模块加载
    • 实现灵活的功能组合
  2. 模块化设计

    • 每个功能作为独立模块添加
    • 模块间通过定义良好的接口交互
  3. 渐进式迁移

    • 支持Rust和C++实现并存
    • 兼容层确保平稳过渡
  4. 生命周期管理

    • 明确的启动顺序控制
    • 关键资源的状态验证
  5. 诊断支持

    • 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_thread
  management_thread_ = new Thread("management_thread", Thread::Priority::NORMAL);
  // 获取到 management_thread 的 handler
  handler_ = 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 都要执行那几步:

  1. 创建module 实体

    • Module* instance = module->ctor_();
  2. 将 当前 module 实体和 gd_stack_thread 线程绑定

    • set_registry_and_handler(instance, thread);
  3. 启动当前模块所依赖的所有子模块。

    • instance->ListDependencies(&instance->dependencies_);
    • Start(&instance->dependencies_, thread);
  4. 最后调用自己的 Start() 函数

    • instance->Start();
  5. 将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 down
  RemovePidFile();

  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 up
  if (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 , 这里不再继续展开分析了。

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

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

相关文章

Matlab 调制信号和fft变换

1、内容简介 Matlab 194-调制信号和fft变换 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

100M/1000M 以太网静电浪涌防护方案

方案简介 以太网是一种生产较早且广泛应用的局域网通讯方式&#xff0c;同时也是一种协议&#xff0c;其核 心在于实现区域内&#xff08;如办公室、学校等&#xff09;的网络互联。根据数据传输速度的不同&#xff0c;以 太网大致可以划分为几个等级&#xff1a;标准以太网…

C语言中while的相关题目

一、题目引入 以下程序中,while循环的循环次数是多少次? 二、代码分析 首先要明确的一点 while循环是当循环条件为真 就会一直循环 不会停止 while中i是小于10的 说明i可以取到0 1 2 3 4 5 6 7 8 9 进入第一个if判断i小于1为真时执行continue i0是为真的 执行continue 后…

「Unity3D」图片导入选项取消Read/Write,就无法正确显示导入大小,以及Addressable打包无法正确显示的问题

如果在Edit -> Project Settings -> Editor中的“Load texture data on demand”勾选&#xff0c;就会让图片导入设置中&#xff0c;不勾选Read/Write&#xff0c;就无法正确显示纹理的大小数字。 更进一步的问题是&#xff0c;使用Addressable打包的时候&#xff0c; 如…

Xcode为不同环境配置不同的环境变量

一般有三种方式&#xff1a; 一、通过多Target 二、通过scheme,也就是多configurations 三、通过.xcconfig文件 先来看第二种方式&#xff1a;通过scheme,也就是多configurations,包括自定义User-settings 第一步&#xff1a;增加configurations,Xcode默认为我们生成了…

阿里通义实验室发布图片数字人项目LAM,实现高保真重建

简介 LAM项目结合了3D Gaussian Splatting&#xff08;高斯点云渲染&#xff09;和大规模预训练模型的优势&#xff0c;解决了传统头部重建方法效率低、依赖多数据的痛点。其背景源于AI生成内容&#xff08;AIGC&#xff09;领域对实时、高保真3D头像生成的需求&#xff0c;尤其…

镜像端口及观察端口的配置

配好路由器的各个接口的IP PC1ping PC3的IP&#xff0c;在路由器中抓2/0/0端口的包&#xff0c;可观察到无结果 输入observe-port interface g 2/0/0 命令配置观察端口 输入mirror to observe-port both命令 &#xff08;其中both表示接收来去的数据包&#xff0c;inboun…

STM32——I2C通讯(软件模拟)

I2C概念 I2C:Inter-Integrated Circuit&#xff08;内部集成电路&#xff09; Philps公司80年代初期开发的&#xff0c;引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性广泛地使用在系统内多个集成电路&#xff08;IC&#xff09;间的低速通讯 简单的双向两线制总线协议…

JetBrains Terminal 又发布新架构,Android Studio 将再次迎来新终端

不到一年的时间&#xff0c;JetBrains 又要对 Terminal 「大刀阔斧」&#xff0c;本次发布的新终端是重构后的全新的架构&#xff0c;而上一次终端大调整还是去年 8 月的 v2024.2 版本&#xff0c;并且在「Android Studio Ladybug | 2024.2.1」也被引入。 不知道你们用不用内置…

论文:Generalized Category Discovery with Large Language Models in the Loop

论文下载地址&#xff1a;Generalized Category Discovery with Large Language Models in the Loop - ACL Anthology 1、研究背景 尽管现代机器学习系统在许多任务上取得了优异的性能&#xff0c;绝大多数都遵循封闭世界的设置&#xff0c;假设训练和测试数据来自同一组预定义…

第十六届蓝桥杯 省赛C/C++ 大学B组

编程题目现在在洛谷上都可以提交了。 未完待续&#xff0c;写不动了。 C11 编译命令 g A.cpp -o A -Wall -lm -stdc11A. 移动距离 本题总分&#xff1a;5 分 问题描述 小明初始在二维平面的原点&#xff0c;他想前往坐标 ( 233 , 666 ) (233, 666) (233,666)。在移动过程…

【计网】网络交换技术之分组交换(复习自用,重要1)

复习自用的&#xff0c;处理得比较草率&#xff0c;复习的同学或者想看基础的同学可以看看&#xff0c;大佬的话可以不用浪费时间在我的水文上了 另外两种交换技术可以直接点击链接访问相关笔记&#xff1a; 电路交换 报文交换 一、分组交换的定义 1.定义 分组交换&#x…

解密CHASE-SQL和XiYan-SQL多智能体AI如何最终实现TEXT2SQL的突破

想象一个世界,无论技术背景如何,任何人都能轻松查询海量数据库、挖掘深层洞察。比如:“我想知道安徽地区最畅销电子产品的第三季度销售额?”——只需一句话。“去年营销支出与客户获取成本之间的相关性如何?”——像聊天一样输入问题。这就是Text-to-SQL的承诺:将人类语言…

思考力提升的黄金标准:广度、深度与速度的深度剖析

文章目录 引言一、广度的拓展&#xff1a;构建多元知识网络1.1 定义与重要性1.2 IT技术实例与提升策略小结&#xff1a;构建多元知识网络&#xff0c;提升IT领域思考力广度 二、深度的挖掘&#xff1a;追求知识的精髓2.1 定义与重要性2.2 IT技术实例与提升策略小结&#xff1a;…

web自动化:下拉选择框、弹出框、滚动条的操作

web自动化&#xff1a;下拉选择框、弹出框、滚动条的操作 一、下拉选择框 1、导包 from selenium.webdriver.support.select inport Select 2、实例化对象 Select(element) 3、常用方法 通过option索引来定位&#xff0c;从0开始&#xff1a;select_by_index(index)通过…

数字人:打破次元壁,从娱乐舞台迈向教育新课堂(4/10)

摘要&#xff1a;数字人正从娱乐领域的璀璨明星跨界到教育领域的智慧导师&#xff0c;展现出无限潜力。从虚拟偶像、影视游戏到直播短视频&#xff0c;数字人在娱乐产业中大放异彩&#xff0c;创造巨大商业价值。在教育领域&#xff0c;数字人助力个性化学习、互动课堂和虚拟实…

互联网三高-数据库高并发之分库分表ShardingJDBC

1 ShardingJDBC介绍 1.1 常见概念术语 ① 数据节点Node&#xff1a;数据分片的最小单元&#xff0c;由数据源名称和数据表组成 如&#xff1a;ds0.product_order_0 ② 真实表&#xff1a;再分片的数据库中真实存在的物理表 如&#xff1a;product_order_0 ③ 逻辑表&#xff1a…

Android游戏逆向工程全面指南

文章目录 第一部分&#xff1a;基础概念与环境搭建1.1 游戏逆向工程概述1.2 法律与道德考量1.3 开发环境准备基础工具集&#xff1a;环境配置示例&#xff1a; 第二部分&#xff1a;静态分析技术2.1 APK反编译与资源提取使用Apktool解包&#xff1a;关键文件分析&#xff1a; 2…

antv x6使用(支持节点排序、新增节点、编辑节点、删除节点、选中节点)

项目需要实现如下效果流程图&#xff0c;功能包括节点排序、新增节点、编辑节点、删除节点、选中节点等 html部分如下&#xff1a; <template><div class"MindMapContent"><el-button size"small" click"addNode">新增节点&…

榕壹云在线商城系统:基于THinkPHP+ Mysql+UniApp全端适配、高效部署的电商解决方案

项目背景&#xff1a;解决多端电商开发的痛点 随着移动互联网的普及和用户购物习惯的碎片化&#xff0c;传统电商系统面临以下挑战&#xff1a; 1. 多平台适配成本高&#xff1a;需要同时开发App、小程序、H5等多端应用&#xff0c;重复开发导致资源浪费。 2. 技术依赖第三方…