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

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

1. 背景

我们在 gd_shim_module 介绍章节中,看到 我们将 HciHal 模块加入到了 modules 中。

modules.add<hal::HciHal>();

在 ModuleRegistry::Start 函数中我们对 加入的所有 module 挨个初始化。
而在该函数中启动一个 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;

本篇文章就拿 hal::HciHal 模块来具体分析一下 他的启动。

2. modules.add

我们先来看一下 在调用 modules.add 时, 到底做了那些事情。

modules.add<hal::HciHal>();
class ModuleList {friend Module;friend ModuleRegistry;public:template <class T>void add() {list_.push_back(&T::Factory); // add 时 添加的是 hal::HciHal::Factory}private:std::vector<const ModuleFactory*> list_;
};
  • 从代码中不难发现, 我们是将 hal::HciHal::Factory 加入到 list_ 中的。
// system/gd/hal/hci_hal_android_hidl.cc
const ModuleFactory HciHal::Factory = ModuleFactory([]() { return new HciHalHidl(); }); // 这里在创建 ModuleFactory 对象时, 传入了一个 函数, 这个函数 去 new HciHalHidl 对象
  • 这里在创建 ModuleFactory 对象时, 传入了一个 函数,但是并没有去调用这个函数。
    • 这个函数的目的是 去 new HciHalHidl 对象
class ModuleFactory {friend ModuleRegistry;friend FuzzTestModuleRegistry;public:ModuleFactory(std::function<Module*()> ctor);private:std::function<Module*()> ctor_;
};//  system/gd/module.cc
ModuleFactory::ModuleFactory(std::function<Module*()> ctor) : ctor_(ctor) {
}
  • 在创建 ModuleFactory 对象时, 也仅仅是将 如下的函数赋值给了 ModuleFactory::ctor_ 函数指针。
[]() { return new HciHalHidl(); }

3. 模块具体启动流程

1. 创建module 实体

  1. 创建module 实体
    • Module* instance = module->ctor_();
[]() { return new HciHalHidl(); }
  • 这里就会去实际触发 该函数,去创建 HciHalHidl 对象。
  • 也就是说, modules.addhal::HciHal() 模块对应的 实体其实是 HciHalHidl对象。

class HciHalHidl : public HciHal {}
  • HciHalHidl 继承 HciHal
class HciHal : public ::bluetooth::Module {}
  • HciHal 又继承 Module

2. 将 当前 module 实体和 gd_stack_thread 线程绑定

  1. 将 当前 module 实体和 gd_stack_thread 线程绑定
    • set_registry_and_handler(instance, 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_ 中。
Handler* Module::GetHandler() const {ASSERT_LOG(handler_ != nullptr, "Can't get handler when it's not started");return handler_;
}
  • 通过 Module::GetHandler() 来获取当前 handler_

3.启动当前模块所依赖的所有子模块

  1. 启动当前模块所依赖的所有子模块。
    • instance->ListDependencies(&instance->dependencies_);
    • Start(&instance->dependencies_, thread);
// system/gd/hal/hci_hal_android_hidl.ccvoid ListDependencies(ModuleList* list) const {list->add<SnoopLogger>();if (common::init_flags::btaa_hci_is_enabled()) {list->add<activity_attribution::ActivityAttribution>();}}
  • 这里调用了 HciHalHidl对象的ListDependencies , 他只依赖SnoopLogger 模块

Start(&instance->dependencies_, thread);

  • 这里会先去启动 SnoopLogger 模块

4. 最后调用自己的 Start() 函数

  1. 最后调用自己的 Start() 函数
    • instance->Start();
  // system/gd/hal/hci_hal_android_hidl.ccvoid Start() override {if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_ = GetDependency<activity_attribution::ActivityAttribution>();}btsnoop_logger_ = GetDependency<SnoopLogger>();LOG_INFO("GetService start.");// 这里 创建一个定时器, 此时将 gd_stack_thread 对应的 handle 传递给 了定时器auto get_service_alarm = new os::Alarm(GetHandler());// 定时器定时 1s, 如果超时,将跑在 gd_stack_thread 线程中get_service_alarm->Schedule(BindOnce([] {LOG_ALWAYS_FATAL("Unable to get a Bluetooth service after 500ms, start the HAL before starting Bluetooth");}),std::chrono::milliseconds(1000));// 向 SM 获取 IBluetoothHci_1_0 服务。std::string instance = GetHciInstance();bt_hci_1_1_ = IBluetoothHci::getService(instance);if (bt_hci_1_1_ != nullptr) {bt_hci_ = bt_hci_1_1_;} else {bt_hci_ = IBluetoothHci_1_0::getService(instance);}// 如果成功 向 SM获取hidl 服务, 就取消 之前的定时器,否则,1s超时后,报错,无法访问到 hidl 服务。get_service_alarm->Cancel();delete get_service_alarm;LOG_INFO("GetService Done.");// 向 蓝牙 hal 进程注册死亡回调ASSERT(bt_hci_ != nullptr);auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal进程,会掉我们的 接口if (bt_hci_1_1_ != nullptr) {bt_hci_1_1_->initialize_1_1(callbacks_);} else {// 掉hal 接口, 触发 hal 初始化,将回调接口告知 hal进程。bt_hci_->initialize(callbacks_);}// Don't timeout here, time out at a higher layercallbacks_->GetInitPromise()->get_future().wait();}
  • callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal进程,会掉我们的 接口

1. InternalHciCallbacks

我们来看一下, 我们向 hal 进程, 注册的回调 长啥样子。

class InternalHciCallbacks : public IBluetoothHciCallbacks {}
  • InternalHciCallbacks 继承 IBluetoothHciCallbacks 接口。
class InternalHciCallbacks : public IBluetoothHciCallbacks {public:InternalHciCallbacks(activity_attribution::ActivityAttribution* btaa_logger_, SnoopLogger* btsnoop_logger): btaa_logger_(btaa_logger_), btsnoop_logger_(btsnoop_logger) {init_promise_ = new std::promise<void>();}void SetCallback(HciHalCallbacks* callback) {ASSERT(callback_ == nullptr && callback != nullptr);callback_ = callback;}void ResetCallback() {callback_ = nullptr;}std::promise<void>* GetInitPromise() {return init_promise_;}// 当 hal 初始化完后,会调用它,通知我们Return<void> initializationComplete(HidlStatus status) {common::StopWatch stop_watch(__func__);ASSERT(status == HidlStatus::SUCCESS);init_promise_->set_value();return Void();}// 当 收到 hci event 后, 会回调我们Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {common::StopWatch stop_watch(GetTimerText(__func__, event));std::vector<uint8_t> received_hci_packet(event.begin(), event.end());btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::EVT);}if (callback_ != nullptr) {callback_->hciEventReceived(std::move(received_hci_packet));}return Void();}// 当收到  acl 数据后,会回调我们Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {common::StopWatch stop_watch(GetTimerText(__func__, data));std::vector<uint8_t> received_hci_packet(data.begin(), data.end());btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::ACL);}if (callback_ != nullptr) {callback_->aclDataReceived(std::move(received_hci_packet));}return Void();}// 当收到 sco 语音数据,会回调给我们Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) override {common::StopWatch stop_watch(GetTimerText(__func__, data));std::vector<uint8_t> received_hci_packet(data.begin(), data.end());btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::SCO);}if (callback_ != nullptr) {callback_->scoDataReceived(std::move(received_hci_packet));}return Void();}// 当收到 leaudio 数据 会回调我们Return<void> isoDataReceived(const hidl_vec<uint8_t>& data) override {common::StopWatch stop_watch(GetTimerText(__func__, data));std::vector<uint8_t> received_hci_packet(data.begin(), data.end());btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ISO);if (callback_ != nullptr) {callback_->isoDataReceived(std::move(received_hci_packet));}return Void();}private:std::promise<void>* init_promise_ = nullptr;HciHalCallbacks* callback_ = nullptr;activity_attribution::ActivityAttribution* btaa_logger_ = nullptr;SnoopLogger* btsnoop_logger_ = nullptr;
};

5.将module 实体加入到 started_modules_

  1. 将module 实体加入到 started_modules_
    • started_modules_[module] = instance;

4. HciHalHidl 模块其他接口介绍


// system/gd/hal/hci_hal_android_hidl.cc
class HciHalHidl : public HciHal {public:void registerIncomingPacketCallback(HciHalCallbacks* callback) override {callbacks_->SetCallback(callback);}void unregisterIncomingPacketCallback() override {callbacks_->ResetCallback();}void sendHciCommand(HciPacket command) override {btsnoop_logger_->Capture(command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(command, SnoopLogger::PacketType::CMD);}bt_hci_->sendHciCommand(command);}void sendAclData(HciPacket packet) override {btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(packet, SnoopLogger::PacketType::ACL);}bt_hci_->sendAclData(packet);}void sendScoData(HciPacket packet) override {btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(packet, SnoopLogger::PacketType::SCO);}bt_hci_->sendScoData(packet);}void sendIsoData(HciPacket packet) override {if (bt_hci_1_1_ == nullptr) {LOG_ERROR("ISO is not supported in HAL v1.0");return;}btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ISO);bt_hci_1_1_->sendIsoData(packet);}
}

1.registerIncomingPacketCallback

  void registerIncomingPacketCallback(HciHalCallbacks* callback) override {callbacks_->SetCallback(callback);}
  • 在之前分析 instance->Start(); 调用时有如下代码
    • callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal进程,会掉我们的 接口
    • bt_hci_->initialize(callbacks_); 将 InternalHciCallbacks 注册进 hal 进程

当 hal 进程中有 hci event 上报时, 先调用到 InternalHciCallbacks 的hciEventReceived, 从而触发 callback_->hciEventReceived(std::move(received_hci_packet));
而这里的 callback_ 就是通过 registerIncomingPacketCallback 调用 callbacks_->SetCallback(callback); 注册进来的。

  // system/gd/hal/hci_hal_android_hidl.ccvoid SetCallback(HciHalCallbacks* callback) {ASSERT(callback_ == nullptr && callback != nullptr);callback_ = callback;}
  // 当 收到 hci event 后, 会回调我们Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {common::StopWatch stop_watch(GetTimerText(__func__, event));std::vector<uint8_t> received_hci_packet(event.begin(), event.end());btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::EVT);}if (callback_ != nullptr) {callback_->hciEventReceived(std::move(received_hci_packet)); // 继续调用 hciEventReceived}return Void();}

那是谁调用的 registerIncomingPacketCallback

// system/gd/hci/hci_layer.cc
void HciLayer::Start() {hal_callbacks_ = new hal_callbacks(*this);hal->registerIncomingPacketCallback(hal_callbacks_);}
  • 是在初始化 HciLayer 模块的 Start 函数中调用的。本机只分析 HciHal模块, HciLayer 模块在其他文章中分析。 这里不再展开分析。 只做记录。
// system/gd/hci/hci_layer.cc
struct HciLayer::hal_callbacks : public hal::HciHalCallbacks {hal_callbacks(HciLayer& module) : module_(module) {}void hciEventReceived(hal::HciPacket event_bytes) override {auto packet = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(event_bytes));EventView event = EventView::Create(packet);// 这里之前, 都是在Binder 线程池中执行的, 应为涉及到 跨进程间通信// 调用 module_.CallOn 将从 Binder线程, 的任务抛给 模块的线程处理, 这里就是  gd_stack_thread 线程module_.CallOn(module_.impl_, &impl::on_hci_event, move(event));}
};
// system/gd/module.hvoid CallOn(T* obj, Functor&& functor, Args&&... args) {GetHandler()->CallOn(obj, std::forward<Functor>(functor), std::forward<Args>(args)...);}
  • GetHandler() 返回的就是 gd_stack_thread 线程 对应的 handler

所以 最终 hal进程的回调, 先调用到 HciHal 模块的 callback , 最终在调用到 HciLayer::hal_callbacks。 这样逐级向上调用。

2. sendHciCommand

// system/gd/hci/hci_layer.cc
void send_next_command() {hal_->sendHciCommand(*bytes);
}// system/gd/hal/hci_hal_android_hidl.cc
void sendHciCommand(HciPacket command) override {btsnoop_logger_->Capture(command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);if (common::init_flags::btaa_hci_is_enabled()) {btaa_logger_->Capture(command, SnoopLogger::PacketType::CMD);}bt_hci_->sendHciCommand(command);}
  • HciLayer::send_next_command 调用 HciHalHidl::sendHciCommand 通过 hal 接口 发送命令给 hal 进程。

3. sendAclData

  // HciLayer// system/gd/hci/hci_layer.ccvoid on_outbound_acl_ready() {auto packet = acl_queue_.GetDownEnd()->TryDequeue();std::vector<uint8_t> bytes;BitInserter bi(bytes);packet->Serialize(bi);hal_->sendAclData(bytes); // 调用 HciHalHidl 层}

4. sendScoData

  // HciLayer// system/gd/hci/hci_layer.ccvoid on_outbound_sco_ready() {auto packet = sco_queue_.GetDownEnd()->TryDequeue();std::vector<uint8_t> bytes;BitInserter bi(bytes);packet->Serialize(bi);hal_->sendScoData(bytes); // 调用 HciHalHidl 层}

5. sendIsoData

// HciLayer
// system/gd/hci/hci_layer.cc
void on_outbound_iso_ready() {auto packet = iso_queue_.GetDownEnd()->TryDequeue();std::vector<uint8_t> bytes;BitInserter bi(bytes);packet->Serialize(bi);hal_->sendIsoData(bytes); // 调用 HciHalHidl 层}

相关文章:

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

1. 背景 我们在 gd_shim_module 介绍章节中&#xff0c;看到 我们将 HciHal 模块加入到了 modules 中。 modules.add<hal::HciHal>();在 ModuleRegistry::Start 函数中我们对 加入的所有 module 挨个初始化。 而在该函数中启动一个 module 都要执行那下面几步&#xff…...

Git 远程仓库

Git 入门笔记 远程仓库 Git 远程仓库 Git 远程仓库是一个托管在网络服务器上的代码仓库&#xff0c;它是团队协作开发的核心。 通过远程仓库&#xff0c;开发者可以共享代码、同步更新&#xff0c;实现分布式协作。 SSH 密钥 SSH 密钥可以让你在使用 Git 时安全地连接远程…...

209.长度最小的子数组- 力扣(LeetCode)

题目&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0 。 示例 1&#xff1a;…...

符号右移“ >>= “ 与 无符号右移“ >>>= “ 的区别

符号右移" >> " 与 无符号右移" >>> " 的区别 一、符号右移" >> " 与 无符号右移" >>> " 的区别1. 符号右移&#xff08;>>&#xff09;与无符号右移&#xff08;>>>&#xff09;的区别…...

山东大学软件学院项目实训-基于大模型的模拟面试系统-专栏管理部分

本周我的主要任务是关于专栏管理部分的完善。 专栏图片的显示问题 问题分析 根据代码可知&#xff1a;图片URL来自于portfolio.headImgUrl&#xff0c;而且如果URL不存在的话&#xff0c;应该显示的是无图片&#xff0c;而网页中显示加载失败说明portfolio.headImgUrl应该是存…...

从 SYN Flood 到 XSS:常见网络攻击类型、区别及防御要点

常见的网络攻击类型 SYN Flood、DoS&#xff08;Denial of Service&#xff09; 和 DDoS&#xff08;Distributed Denial of Service&#xff09; 是常见的网络攻击类型&#xff0c;它们的目标都是使目标系统无法正常提供服务。以下是它们的详细说明&#xff1a; 1. SYN Flood…...

ros2-rviz2控制unity仿真的6关节机械臂,探索从仿真到实际应用的过程

文章目录 前言&#xff08;Introduction&#xff09;搭建开发环境&#xff08;Setup Development Environment&#xff09;在window中安装Unity&#xff08;Install Unity in window&#xff09;创建Docker容器&#xff0c;并安装相关软件&#xff08;Create Docker containers…...

01_通过调过api文字生成音频示例

第1 第2 第3&#xff0c;测试音色 第4 第5 第6 第7生成api_key 第8代码 import requestsurl "https://api.siliconflow.cn/v1/audio/speech"payload {"input": "在中国传统文化中&#xff0c;谦让被视为一种美德&#xff0c;但过度的让步…...

使用PyTorch实现目标检测边界框转换与可视化

一、引言 在目标检测任务中&#xff0c;边界框&#xff08;Bounding Box&#xff09;的坐标表示与转换是核心基础操作。本文将演示如何&#xff1a; 实现边界框的两种表示形式&#xff08;角点坐标 vs 中心坐标&#xff09;之间的转换 使用Matplotlib在图像上可视化边界框 验…...

QEMU学习之路(8)— ARM32通过u-boot 启动Linux

QEMU学习之路&#xff08;8&#xff09;— ARM32通过u-boot 启动Linux 一、前言 参考文章&#xff1a; Linux内核学习——内核的编译和启动 Linux 内核的编译和模拟执行 Linux内核运行——根文件系统 Linux 内核学习——使用 uboot 加载内核 二、构建Linux内核 1、获取Linu…...

flutter 桌面应用之右键菜单

​在 Flutter 桌面应用开发中&#xff0c;context_menu 和 contextual_menu 是两款常用的右键菜单插件&#xff0c;各有特色。以下是对它们的对比分析&#xff1a;​ &#x1f9e9; context_menu 集成方式&#xff1a;​通过 ContextMenuArea 组件包裹目标组件&#xff0c;定义…...

系统设计面试总结:高性能相关:CDN(内容分发网络)、什么是静态资源、负载均衡(Nginx)、canal、主从复制

以下为本人自学回顾使用&#xff0c;请支持javaGuide原版。 1.CDN概述 CDN 就是将静态资源分发到多个不同的地方以实现就近访问&#xff0c;进而加快静态资源的访问速度&#xff0c;减轻服务器以及带宽的负担。 你可以将 CDN 看作是服务上一层的特殊缓存服务&#xff0c;分布…...

从红黑树到哈希表:原理对比与典型场景应用解析(分布式以及布隆过滤器)

在数据结构的世界里&#xff0c;红黑树一直以「自平衡二叉查找树」的身份备受赞誉。凭借红黑节点的精妙设计&#xff0c;它能将插入、删除、查找的时间复杂度稳定控制在 ( log ⁡ n ) (\log n) (logn)&#xff0c;成为处理有序数据的经典方案。然而&#xff0c;当业务场景对「…...

动手学深度学习:手语视频在VGG模型中的测试

前言 其他所有部分同上一篇AlexNet一样&#xff0c;所以就不再赘诉&#xff0c;直接看VGG搭建部分。 模型 VGG是第一个采取块进行模块化搭建的模型。 def vgg_block(num_convs,in_channels,out_channels):layers[]for _ in range(num_convs):layers.append(nn.Conv2d(in_ch…...

微信小程序实战案例 - 餐馆点餐系统 阶段 4 - 订单列表 状态

✅ 阶段 4 – 订单列表 & 状态 目标 展示用户「我的订单」列表支持状态筛选&#xff08;全部 / 待处理 / 已完成&#xff09;支持分页加载和实时刷新使用原生组件编写 ✅ 1. 页面结构&#xff1a;文件结构 pages/orders/├─ index.json├─ index.wxml├─ index.js└─…...

深度学习理论-直观理解 Attention

本文首先介绍 Attention 的原始公式&#xff0c;然后以 Self-Attention 为例&#xff0c;简化后逐步分析 Attention 计算结果表达的含义 Attention Attention 公式如下&#xff1a; A t t e n t i o n s o f t m a x ( Q ⋅ K T d k ) ⋅ V Attention softmax(\frac{Q \cd…...

python中 “with” 关键字的取舍问题

自动管理资源&#xff08;自动关闭文件&#xff09; 当你使用 with 打开文件时&#xff0c;文件会在 with 代码块结束后自动关闭&#xff0c;无论是否发生异常。这意味着你不需要显式地调用 f.close() 来关闭文件 示例&#xff1a; with open("words.txt", "r…...

ISIS协议(动态路由协议)

ISIS基础 基本概念 IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP&#xff08;ConnectionL…...

llm开发框架新秀

原文链接:https://i68.ltd/notes/posts/20250404-llm-framework3/ google开源ADK-Agent Development Kit 开源的、代码优先的 Python 工具包&#xff0c;用于构建、评估和部署具有灵活性和控制力的复杂智能体项目仓库:https://github.com/google/adk-python 2.6k项目文档:Age…...

Zookeeper的典型应用场景?

大家好&#xff0c;我是锋哥。今天分享关于【Zookeeper的典型应用场景?】面试题。希望对大家有帮助&#xff1b; Zookeeper的典型应用场景? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 是一个开源的分布式协调服务&#xff0c;主要用于管理和协调大…...

【C数据结构】 TAILQ双向有尾链表的详解

TAILQ双向有尾链表的详解 常见的链表结构1.SLIST2.STAILQ3.LIST4.TAILQ5.CIRCLEQ 一、TAILQ链表简介二、TAILQ的定义和声明三、TAILQ队列的函数1.链表头的初始化2.获取第一个节点地址3.获取最后一个节点地址4.链表是否为空5.下一个节点地址6.上一个节点地址7.插入头节点8.插入尾…...

redisson的unlock方法

//分布式方式&#xff0c;分布式锁&#xff0c;采用redisson锁 RLock lock redissonClient.getLock(userId.toString());//lock方法会无限重试。getLock底层是hash&#xff0c;大key是userid&#xff0c;小key是线程&#xff0c;value是重入次数 try {//boolean b lock.tryLo…...

ffmpeg 切割视频失败 ffmpeg 命令参数 -vbsf 在新版本中已经被弃用,需要使用 -bsf:v 替代

ffmpeg 切割视频失败 ffmpeg 命令参数 -vbsf 在新版本中已经被弃用&#xff0c;需要使用 -bsf:v 替代 从日志中可以看到问题出在第一个 ffmpeg 命令执行时&#xff1a; Unrecognized option vbsf.Error splitting the argument list: Option not found这是因为 ffmpeg 命令参…...

设计模式——抽象工厂模式总结

理解了前面的工厂模式后&#xff0c;再理解抽象工厂模式就很容易了。 工厂模式&#xff1a;https://blog.csdn.net/inside802/article/details/147170118?spm1011.2415.3001.10575&sharefrommp_manage_link 抽象工厂模式就是工厂模式的更加抽象化&#xff0c;父类不仅不承…...

JavaScript 定时器

在 JavaScript 中&#xff0c;定时器是实现代码在特定时间间隔执行或延迟执行的重要工具。下面我们将深入探讨定时器的相关知识。 定时器基础 setTimeout() setTimeout() 函数用于在指定的延迟时间后执行一次回调函数。它接受两个参数&#xff0c;第一个参数是要执行的回调函…...

企业经营决策风险

在企业的经营过程中&#xff0c;领导者每天都在面对大量的决策——该扩大生产还是收缩业务&#xff1f;该增设销售渠道还是提升产品质量&#xff1f;但你知道吗&#xff0c;企业最大的成本&#xff0c;不是生产成本&#xff0c;也不是人工成本&#xff0c;而是决策错误的成本&a…...

【云安全】云原生-centos7搭建/安装/部署k8s1.23.6单节点

一、节点基本配置 1、准备操作系统 2、 修改主机名 hostnamectl set-hostname master-1 hostnamectl set-hostname node1 hostnamectl set-hostname node2#验证hostnamectl status 3、修改/etc/hosts cat <<EOF >>/etc/hosts 192.168.255.137 master-1 192.168…...

【已更新完毕】2025泰迪杯数据挖掘竞赛B题数学建模思路代码文章教学:基于穿戴装备的身体活动监测

基于穿戴装备的身体活动监测 摘要 本研究基于加速度计采集的活动数据&#xff0c;旨在分析和统计100名志愿者在不同身体活动类别下的时长分布。通过对加速度数据的处理&#xff0c;活动被划分为睡眠、静态活动、低强度、中等强度和高强度五类&#xff0c;进而计算每个志愿者在…...

力扣每日打卡 1922. 统计好数字的数目 (中等)

力扣 1922. 统计好数字的数目 中等 前言一、题目内容二、解题方法1. 暴力解法&#xff08;会超时&#xff0c;此法不通&#xff09;2. 快速幂运算3. 组合计数的思维逻辑分析组合计数的推导例子分析思维小结论 4.官方题解4.1 方法一&#xff1a;快速幂 三、快速幂运算快速幂运算…...

宝塔Linux面板 - 添加站点建站时没有域名实现 IP 地址访问测试(宝塔面板建站 IP 访问)

前言 使用面板添加站点时&#xff0c;必须要填写一个域名用来指向程序&#xff0c;没有域名怎么办&#xff1f; 答案&#xff1a;域名直接写 【服务器 IP 地址】 操作步骤 如果还没有添加站点&#xff0c;则直接在创建站点的时候&#xff0c;域名那填写服务器地址即可&#…...

【GitHub探索】mcp-go,MCP协议的Golang-SDK

近期大模型Agent应用开发方面&#xff0c;MCP的概念比较流行&#xff0c;基于MCP的ToolServer能力开发也逐渐成为主流趋势。由于笔者工作原因&#xff0c;主力是Go语言&#xff0c;为了调研大模型应用开发&#xff0c;也接触到了mcp-go这套MCP的SDK实现。 对于企业内部而言&am…...

手撕TCP内网穿透及配置树莓派

注意&#xff1a; 本文内容于 2025-04-13 15:09:48 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;手撕TCP内网穿透及配置树莓派。感谢您的关注与支持&#xff01; 之前入手了树莓派5&#xff0c;…...

人形机器人运动与操作: 控制、规划和学习方面的当前进展与挑战

前言 图 1&#xff1a;执行运动和操作任务的人形机器人&#xff1a;&#xff08;a&#xff09;HRP-4 在适应地形的同时擦拭木板[1]&#xff1b;&#xff08;b-g&#xff09;Digit、Hector[2]、Atlas、H1、Justin[3]和 Apollo 取放物体&#xff1b;&#xff08;h&#xff09;iCu…...

C++ 重构muduo网络库

本项目参考的陈硕老师的思想 1. 基础概念 进程里有 Reactor、Acceptor、Handler 这三个对象 Reactor 对象的作用是监听和分发事件&#xff1b;Acceptor 对象的作用是获取连接&#xff1b;Handler 对象的作用是处理业务&#xff1b; 先说说 阻塞I/O&#xff0c;非阻塞I/O&…...

【计算机网络实践】(十二)大学校园网综合项目设计

本系列包含&#xff1a; &#xff08;一&#xff09;以太网帧分析与网际互联协议报文结构分析 &#xff08;二&#xff09;地址解析协议分析与传输控制协议特性分析 &#xff08;三&#xff09;交换机的基本操作、配置、 虚拟局域网配置和应用 &#xff08;四&#xff09;交…...

通过api程序的方式编辑ps的三种方式

目前只使用了第一种 ps-python-api去操作 还没有尝试其他两种方式对于第一种方式必须要开启ps程序&#xff0c;程序调用修改新增图层和文档时会同步到ps页面&#xff0c;可以直观看到修改结果...

论文阅读笔记——Reactive Diffusion Policy

RDP 论文 通过 AR 提供实时触觉/力反馈&#xff1b;慢速扩散策略&#xff0c;用于预测低频潜在空间中的高层动作分块&#xff1b;快速非对称分词器实现闭环反馈控制。 ACT、 π 0 \pi_0 π0​ 采取了动作分块&#xff0c;在动作分块执行期间处于开环状态&#xff0c;无法及时响…...

MySQL表的增删改查进阶版

Mysql 1、数据库的约束1.1约束类型1.2 NULL约束1.3 UNIQUE&#xff1a;唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束&#xff08;重点&#xff09;1.6 FOREIGN KEY&#xff1a;外键约束&#xff08;重点&#xff09; 2.表的设计2.1一对一2.2…...

【C#】Socket通信的使用

在C#中&#xff0c;Socket通信是一种用于实现网络通信的底层技术。通过Socket&#xff0c;程序可以在网络上与其他设备进行数据交换。以下是如何使用C#中的System.Net.Sockets命名空间来实现Socket通信的详细步骤。 1. Socket通信的基本概念 Socket: 一个Socket是网络通信的端…...

linux以C方式和内核交互监听键盘[香橙派搞机日记]

最近在深入研究我的香橙派&#xff0c;不可避免的遇到了怎么认识和使用Linux内核的问题。 我给自己留了一个简单的任务&#xff1a;使用原生C来监听内核&#xff0c;实现读取键盘的消息。 CSDN上也有其他文章来解决这个问题&#xff0c;不过要么是技术不达标&#xff08;直接和…...

【C++初学】课后作业汇总复习(七) 指针-深浅copy

1、 HugeInt类:构造、、cout Description: 32位整数的计算机可以表示整数的范围近似为&#xff0d;20亿到&#xff0b;20亿。在这个范围内操作一般不会出现问题&#xff0c;但是有的应用程序可能需要使用超出上述范围的整数。C可以满足这个需求&#xff0c;创建功能强大的新的…...

【iOS】UIPageViewController学习

UIPageViewController学习 前言创建一个UIPageViewController最简单的使用 UIPageViewController的方法说明&#xff1a;效果展示 UIPageViewController的协议方法 前言 笔者最近在写项目时想实现一个翻书效果&#xff0c;上网学习到了UIPageViewController今天写本篇博客总结…...

GDB 调试命令详解:高效掌握常用调试技巧

&#x1f41e; GDB 调试命令详解&#xff1a;高效掌握常用调试技巧 GNU Debugger&#xff08;GDB&#xff09;是 Linux 下最强大的 C/C 调试工具。本文将系统梳理 GDB 的常用命令&#xff0c;覆盖运行控制、断点管理、变量查看、线程与进程调试等核心功能&#xff0c;助你快速掌…...

实验二 用递归下降法分析表达式实验

【实验目的】 1.掌握用递归下降分析法进行语法分析的方法。加深对自顶向下语法分析原理的理解。 2.掌握设计、编制并调试自顶向下语法分析程序的思想和方法。 3.本实验是高级语言程序设计、数据结构和编译原理中词法分析、自顶向下语法分析原理等知 识的综合。由于语法分析…...

【随身wifi】青龙面板保姆级教程

0.操作前必看 本教程基于Debian系统&#xff0c;从Docker环境。面板安装&#xff0c;到最后拉取脚本的使用。 可以拉库跑狗东京豆&#xff0c;elm红包等等&#xff0c;也可以跑写自己写的脚本&#xff0c;自行探索 重要的号别搞&#xff0c;容易黑号&#xff0c;黑号自己负责…...

从一到无穷大 #45:InfluxDB MCP Server 构建:从工程实践到价值重构

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 工程实践遇到的问题MCP Host选择开发流程 结果展现可能性展望工作生活带来的变化 MCP…...

app逆向专题五:新快报app数据采集

app逆向专题五:新快报app数据采集 一、抓包寻找数据接口二、编写代码三、完整代码一、抓包寻找数据接口 打开charles,并在手机端打开新快报app,点击“广州”或者“经济”等选项卡,抓包,寻找数据接口,如图所示: 二、编写代码 这里介绍一种简便的代码编写方法,在数据…...

使用 lm-eval 评估模型时报错:TypeError: ‘NoneType’ object is not callable 的解决方案

问题描述 在使用 lm-evaluation-harness 进行多 GPU 模型评估时&#xff0c;使用如下命令&#xff1a; accelerate launch --multi-gpu --num_processes 2 \-m lm_eval --model hf \--model_args pretrained${local_model_path} \--tasks mmlu \--batch_size 8 \--log_sample…...

脉冲耦合神经网络(PCNN):图像处理中的强大工具

文章目录 一、PCNN 的起源与背景二、PCNN 的基本原理(一)模型结构(二)工作方式(三)动态阈值与脉冲特征三、PCNN 在图像处理中的应用(一)图像分割(二)边缘检测(三)纹理分析四、PCNN 的实现与优化环境准备PCNN 类定义图像分割示例在图像处理和计算机视觉领域,神经网…...

【Git】从零开始使用git --- git 的基本使用

哪怕是野火焚烧&#xff0c;哪怕是冰霜覆盖&#xff0c; 依然是志向不改&#xff0c;依然是信念不衰。 --- 《悟空传》--- 从零开始使用git 了解 Gitgit创建本地仓库初步理解git结构版本回退 了解 Git 开发场景中&#xff0c;文档可能会经历若干版本的迭代。假如我们不进行…...