【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 類的主要功能

  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 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++ 的流程。

主要完成如下功能:

  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_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 都要執行那幾步:

  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 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 , 這里不再繼續展開分析了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77665.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77665.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77665.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【包管理器】主流包管理器_對比_應用場景

不定期更新&#xff0c;建議關注收藏點贊。 鏈接&#xff1a; npm專題 目錄 主流包管理器簡介對比 主流包管理器簡介 主流的包管理器其實有不少&#xff0c;不同語言和平臺都有各自的一套系統。 前端&#xff08;JavaScript/TypeScript&#xff09; 名稱簡介開發者特點npmNo…

參照Spring Boot后端框架實現序列化工具類

本文參照Jackson實現序列化工具類&#xff0c;旨在于簡化開發 JacksonUtil.class public class JacksonUtil {private JacksonUtil() {}/*** 單例*/private final static ObjectMapper OBJECT_MAPPER;static {OBJECT_MAPPER new ObjectMapper();}private static ObjectMappe…

Rust入門之迭代器(Iterators)

Rust入門之迭代器&#xff08;Iterators&#xff09; 本文已同步本人博客網站 本文相關源碼已上傳Github 前言 迭代器&#xff08;Iterators&#xff09;是 Rust 中最核心的工具之一&#xff0c;它不僅是遍歷集合的抽象&#xff0c;更是 Rust 零成本抽象&#xff08;Zero-Co…

若依框架二次開發——RuoYi-AI 本地部署流程

文章目錄 項目環境安裝后端1. 下載項目2. 使用 IDEA 導入項目3. 配置 Maven4. 配置 Maven settings.xml5. 初始化數據庫6. 啟動 Redis7. 修改數據庫配置8. 啟動后端服務安裝管理端1. 下載管理端項目2. 安裝依賴3. 啟動管理端4. 修改管理端配置安裝用戶端1. 下載用戶端項目2. 安…

精品推薦-最新大模型MCP核心架構及最佳實踐資料合集(18份).zip

精品推薦-最新大模型MCP核心架構及最佳實踐資料合集&#xff0c;共18份。 1、2025年程序員必學技能&#xff1a;大模型MCP核心技術.pdf 2、MCP 架構設計剖析&#xff1a;從 Service Mesh 演進到 Agentic Mesh.pdf 3、MCP 架構設計深度剖析&#xff1a;使用 Spring AI MCP 四步…

DataWorks智能體Agent發布!基于MCP實現數據開發與治理自動化運行

在傳統的數據開發工作中&#xff0c;企業用戶或者開發者常常需要進行繁瑣的配置、復雜的代碼撰寫、反復的性能調優和大量重復性的操作&#xff0c;才能實現數據開發、數據集成和數據治理等工作&#xff0c;效率十分低下。 近日&#xff0c;阿里云大數據開發治理平臺DataWorks基…

IDEA 中右側沒有顯示Maven

IDEA 中右側沒有顯示Maven 1. 檢查 Maven 項目是否正確加載 現象 ? 項目是 Maven 項目&#xff0c;但右側沒有 Maven 工具窗口。 ? 項目根目錄下有 pom.xml&#xff0c;但 IDEA 沒有識別為 Maven 項目。 解決方法 手動重新加載 Maven 項目&#xff1a; ? 在 IDEA 中&…

羅技K860鍵盤

羅技藍牙鍵盤的頂部功能鍵F1-F12的原本功能 單擊羅技鍵盤的功能鍵時&#xff0c;默認響應的是鍵盤上面顯示的快進、調節音量等功能。改變回F1~F12原本功能&#xff0c;同時按下 fn和esc組合鍵

什么是大型語言模型(LLM)?哪個大模型更好用?

什么是 LLM&#xff1f; ChatGPT 是一種大型語言模型 (LLM)&#xff0c;您可能對此并不陌生。它以非凡的能力而聞名&#xff0c;已證明能夠出色地完成各種任務&#xff0c;例如通過考試、生成產品內容、解決問題&#xff0c;甚至在最少的輸入提示下編寫程序。 他們的實力現已…

css畫右上角 角標三角形

.corner {position: absolute;top: -2rem;right: -2rem;width: 0;height: 0;border: 2rem solid red;border-bottom-color: transparent;border-top-color: transparent;border-left-color: transparent;transform: rotateZ(135deg); } 基本思路就是設置border&#xff0c;只設…

vue自定義顏色選擇器

vue自定義顏色選擇器 效果圖&#xff1a; step0: 默認寫法 調用系統自帶的顏色選擇器 <input type"color">step1:C:\Users\wangrusheng\PycharmProjects\untitled18\src\views\Home.vue <template><div class"container"><!-- 顏…

[Python] 企業內部應用接入釘釘登錄,端內免登錄+瀏覽器授權登錄

[Python] 為企業網站應用接入釘釘鑒權&#xff0c;實現釘釘客戶端內自動免登授權&#xff0c;瀏覽器中手動釘釘授權登錄兩種邏輯。 操作步驟 企業內部獲得 開發者權限&#xff0c;沒有的話先申請。 訪問 釘釘開放平臺-應用開發 創建一個 企業內部應用-釘釘應用。 打開應用…

[藍橋杯 2023 國 Python A] 整數變換

P10985 [藍橋杯 2023 國 Python A] 整數變換 題目背景 建議使用 PyPy3 提交本題。 題目描述 小藍有一個整數 n n n。每分鐘&#xff0c;小藍的數都會發生變化&#xff0c;變為上一分鐘的數 減去上一分鐘的數的各個數位和。 例如&#xff0c;如果小藍開始時的數為 23 23 …

【Linux】TCP_Wrappers+iptables實現堡壘機功能

規劃 顯示jumpserver的簡單功能&#xff0c;大致的網絡拓撲圖如下 功能規劃 & 拓撲結構 JumpServer&#xff08;堡壘機&#xff09;主要功能&#xff1a; 對訪問目標服務器進行統一入口控制&#xff08;例如 nginx、mysql、redis&#xff09;。使用 iptables 做 NAT 轉…

用HTML和CSS繪制佩奇:我不是佩奇

在這篇博客中&#xff0c;我將解析一個完全使用HTML和CSS繪制的佩奇(Pig)形象。這個項目展示了CSS的強大能力&#xff0c;僅用樣式就能創造出復雜的圖形&#xff0c;而不需要任何圖片或JavaScript。 項目概述 這個名為"我不是佩奇"的項目是一個純CSS繪制的卡通豬形象…

Spring 中 WebFlux 編寫一個簡單的 Controller

引言&#xff1a;響應式編程與 WebFlux 隨著應用程序需要處理大量并發請求的情況越來越多&#xff0c;傳統的 Servlet 編程模式可能無法滿足高效和低延遲的需求。為了應對這種情況&#xff0c;Spring 5 引入了 WebFlux&#xff0c;一個基于響應式編程的 Web 框架&#xff0c;旨…

React十案例下

代碼下載 登錄模塊 用戶登錄 頁面結構 新建 Login 組件&#xff0c;對應結構: export default function Login() {return (<div className{styles.root}><NavHeader className{styles.header}>賬號登錄</NavHeader><form className{styles.form}>&…

100道C#高頻經典面試題帶解析答案——全面C#知識點總結

100道C#高頻經典面試題帶解析答案 以下是100道C#高頻經典面試題及其詳細解析&#xff0c;涵蓋基礎語法、面向對象編程、集合、異步編程、LINQ等多個方面&#xff0c;旨在幫助初學者和有經驗的開發者全面準備C#相關面試。 &#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSD…

機動車號牌管理系統設計與實現(代碼+數據庫+LW)

摘 要 在如今社會上&#xff0c;關于信息上面的處理&#xff0c;沒有任何一個企業或者個人會忽視&#xff0c;如何讓信息急速傳遞&#xff0c;并且歸檔儲存查詢&#xff0c;采用之前的紙張記錄模式已經不符合當前使用要求了。所以&#xff0c;對機動車號牌信息管理的提升&…

VMWare Workstation Pro17.6最新版虛擬機詳細安裝教程(附安裝包教程)

目錄 前言 一、VMWare虛擬機下載 二、VMWare虛擬機安裝 三、運行虛擬機 前言 VMware 是全球領先的虛擬化技術與云計算解決方案提供商&#xff0c;通過軟件模擬計算機硬件環境&#xff0c;允許用戶在一臺物理設備上運行多個獨立的虛擬操作系統或應用。其核心技術可提升硬件…