【android bluetooth 協議分析 02】【bluetooth hal 層詳解 3】【高通藍牙hal主要流程介紹-上】

1. 背景

本節主要討論 高通 藍牙 hal 中,的一些流程。 看看你是否都清楚如下問題:

  1. 高通芯片電如何控制?
  2. 串口是在哪里控制的?
  3. 固件如何下載?
  4. 初始化流程是怎么樣的?

如果你已經對上述討論的問題,已經很清楚了,那你無需閱讀該文章,請自行忽略。當然,也可以給笨叔挑挑錯。 歡迎評論,一起探討,畢竟都是笨叔自己的理解,難免有點出入,我也想進步!!!

在閱讀這篇文章之前, 可以先閱讀一下兩篇文章。
【android bluetooth 框架分析 02】【Module詳解 3】【HciHal 模塊介紹】
【android bluetooth 協議分析 02】【bluetooth hal 層詳解 2】【藍牙hal層hidl_1.0和hidl_1.1層接口的區別是什么】

我們這里按照 hidl 1.0 接口講解。1.1 類似。

2. bt server 觸發流程

我們先來回顧一下,bt.server 是如何觸發 hal 之間的通信的。

在 HciHal 模塊的 Start 函數中,我們會去向 SM 獲取 hal_1.0 服務, 并且調用 hal 的 initialize 接口

  // system/gd/hal/hci_hal_android_hidl.ccvoid Start() override {std::string instance = GetHciInstance();if (bt_hci_1_1_ != nullptr) {} else {// 1. 向 SM 獲取 IBluetoothHci_1_0 服務。bt_hci_ = IBluetoothHci_1_0::getService(instance);}LOG_INFO("GetService Done.");callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal進程,會調用我們的 接口if (bt_hci_1_1_ != nullptr) {} else {// 2. 調用 hal 接口, 觸發 hal 初始化,將回調接口告知 hal進程。bt_hci_->initialize(callbacks_);}// 3. 等待 hal initialize 執行完成。callbacks_->GetInitPromise()->get_future().wait();}

可以將上面 HciHal 模塊的 Start 函數 總結為三步:

  1. 向 SM 找 bt hal 1.0 服務
  2. 調用 hal initialize
  3. 等待 hal initialize 完成

我們本節討論的 藍牙芯片上電,打開藍牙串口, 固件下載。 都是在 這里調用 hal initialize 到 hal initialize 完成直接 進行的。

1. 向 SM 找 bt hal 1.0 服務

std::string GetHciInstance() {char buf[64];int hci_adapter = InitFlags::GetAdapterIndex();// 0 -> "default" (default bluetooth adapter)// 1 -> "hci1" (new bluetooth adapter)if (hci_adapter > 0) {snprintf(buf, sizeof(buf), "hci%d", hci_adapter);} else {snprintf(buf, sizeof(buf), "default");}return std::string(buf);
}
  • 如果是 bt0 就找尋 android.hardware.bluetooth@1.0::IBluetoothHci/default 服務, 如果是 bt1 就找尋 android.hardware.bluetooth@1.0::IBluetoothHci/hci1 服務
bt_hci_ = IBluetoothHci_1_0::getService(instance);
  • 通過 IBluetoothHci_1_0::getService 就能獲取到 android.hardware.bluetooth@1.0::IBluetoothHci/default 服務。

2. 調用 hal initialize

callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_); // hal進程,會調用我們的 接口bt_hci_->initialize(callbacks_);

class InternalHciCallbacks : public IBluetoothHciCallbacks {};
  • InternalHciCallbacks 繼承了 IBluetoothHciCallbacks。 hal 側的回調就是調用到這里。
  • 通過 調用 hal 的 initialize 方法將 bt.server 的回調函數注冊進 hal.

調用 hal 的 initialize 方法 , 將完成很多工作。 完成的工作將在 hal 側詳細介紹。我們這里先梳理流程。

當 hal 側處理完 initialize 后, 會通知 bt.server.繼續執行。

3. 等待 hal initialize 完成

callbacks_->GetInitPromise()->get_future().wait();std::promise<void>* GetInitPromise() {return init_promise_;}
  • 通過 init_promise_ 實現等待。

我們看一下, hal 是如何通知我們的。

class InternalHciCallbacks : public IBluetoothHciCallbacks {Return<void> initializationComplete(HidlStatus status) {common::StopWatch stop_watch(__func__);//ASSERT(status == HidlStatus::SUCCESS);if (status != HidlStatus::SUCCESS) {ALOGE("HidlStatus is not SUCCESS");exit(EXIT_FAILURE);}init_promise_->set_value();return Void();}}

在第2步,我們將 InternalHciCallbacks 注冊進了 hal 進程。 hal 在處理完成 initialize 后,會通過 initializationComplete 告知 .bt.server.

此時,這里的等待就會退出, 繼續執行其他操作。

3. hal 側邏輯

1. hal服務啟動和注冊

android 系統中 會在對應的 rc 文件中啟動當前的 藍牙hal

service vendor.bluetooth-1-0-xxxx /vendor/bin/hw/android.hardware.bluetooth@1.0-service-xxxxinterface android.hardware.bluetooth@1.0::IBluetoothHci defaultclass haluser bluetoothgroup bluetooth system wakelock oem_2901 net_raw oem_2912capabilities BLOCK_SUSPEND NET_ADMIN
// hidl_hci/1.0/default/service.cppint main() {ALOGI("BT-Transport driver main");(void)umask(S_IWGRP | S_IWOTH);struct sched_param rt_params;rt_params.sched_priority = BT_TX_RT_PRIORITY;android::hardware::ProcessState::initWithMmapSize((size_t)(256144));configureRpcThreadpool(1, true /*callerWillJoin*/);ALOGI("isVendorEnhancedFramework: %d", isVendorEnhancedFramework);ALOGI("Registering BT Service");status_t status;status = registerBluetoothHci(); // 注冊藍牙服務if (status != OK)ALOGI("Error while registering BT service: %d", status);ALOGI("BTTPI: Main, joinRpcThreadpool for HIDL");joinRpcThreadpool();return status;
}static status_t registerBluetoothHci()
{status_t status;
#ifdef DUAL_BTconst char *bt_hci_instance = "hci1";
#elseconst char *bt_hci_instance = "default";
#endif#ifdef LAZY_SERVICEstatus = registerLazyPassthroughServiceImplementation<IBluetoothHci>(bt_hci_instance);
#elsestatus = registerPassthroughServiceImplementation<IBluetoothHci>(bt_hci_instance);
#endifreturn status;
}

我們當前的 hal 支持雙藍牙, 從 DUAL_BT 可以看出來,我們通過 編譯來控制 編譯兩個不同的 bin. 用于 bt0 和 bt1.

在 registerBluetoothHci 函數中 當 registerPassthroughServiceImplementation<>() 被調用時:

  1. 框架在 libhidltransport.so 內部調用一個動態查找函數
  2. 它嘗試在你的 HAL .so 中查找函數:

extern “C” IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
3. 如果找到了,就調用它并返回 HAL 實例

// hidl_hci/1.0/default/bluetooth_hci.cppIBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name)
{ALOGI("%s ,new BluetoothHci() name:%s slot:%d", __func__, name, Util::getHwSlot());Util::init(name);return new BluetoothHci(Util::getHwSlot());
}

BluetoothHci 繼承 IBluetoothHci 接口


class BluetoothHci : public IBluetoothHci {public:BluetoothHci();BluetoothHci(int slot);~BluetoothHci();Return<void> initialize(const ::android::sp<IBluetoothHciCallbacks>& cb) override;Return<void> sendHciCommand(const hidl_vec<uint8_t>& packet) override;Return<void> sendAclData(const hidl_vec<uint8_t>& data) override;Return<void> sendScoData(const hidl_vec<uint8_t>& data) override;Return<void> close() override;Return<void> debug(const hidl_handle& handle,const hidl_vec<hidl_string>& options) override;private:void sendDataToController(HciPacketType type, const hidl_vec<uint8_t>& data);void startDetectBluetooth(int slot);void stopDetectBluetooth();::android::sp<IBluetoothHciCallbacks> event_cb_;::android::sp<BluetoothDeathRecipient> deathRecipient;int hw_slot_;std::thread detect_bt_thread_;
};extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
// bt0 調用01-13 02:03:20.629763   714   714 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: HIDL_FETCH_IBluetoothHci ,new BluetoothHci() name:default slot:0// bt1 調用
01-13 02:03:20.630275   715   715 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: HIDL_FETCH_IBluetoothHci ,new BluetoothHci() name:hci1 slot:0

// 此時 bt0 的服務就已經注冊成功了。 bt.server 就可以 get 當前服務了。
01-13 02:03:20.641529   714   714 I HidlServiceManagement: Registered android.hardware.bluetooth@1.0::IBluetoothHci/default

2. initialize 調用

當 bt.server 調用 bt_hci_->initialize(callbacks_); 將調用到如下代碼

// hidl_hci/1.0/default/bluetooth_hci.cppReturn<void> BluetoothHci::initialize(const ::android::sp<IBluetoothHciCallbacks>& cb)
{bool rc = false;ALOGW("BluetoothHci::initialize(), slot%d", hw_slot_);if (cb == nullptr) {ALOGE("%s: Received NULL callback from BT client", __func__);return Void();}::android::sp<IBluetoothHciCallbacks> event_cb_tmp;event_cb_tmp = cb; // 將我們 bt.server 傳遞進來的 回調保存在 event_cb_tmp 中了。// 1. 調用 DataHandler::Init 方法rc = DataHandler::Init( TYPE_BT,[this, event_cb_tmp](bool status) {if (event_cb_tmp != nullptr) {ALOGI("%s: Set callbacks received from BT client inorder ""to provide status and data through them", __func__);event_cb_ = event_cb_tmp;}if (event_cb_ != nullptr) {auto hidl_client_status = event_cb_->initializationComplete(status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback initializationComplete failed");}}},[this, event_cb_tmp](HciPacketType type, const hidl_vec<uint8_t> *packet) {DataHandler *data_handler = DataHandler::Get();if (event_cb_tmp == nullptr) {ALOGE("BluetoothHci: event_cb_tmp is null");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);return;}/* Skip calling client callback when client is dead */if(data_handler && (data_handler->GetClientStatus(TYPE_BT) == false)) {ALOGI("%s: Skip calling client callback when client is dead", __func__);return;}Logger::Get()->UpdateRxTimeStamp();switch (type) {case HCI_PACKET_TYPE_EVENT:{
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_PRE_STACK_EVT_CALL_BACK);
#endifauto hidl_client_status = event_cb_tmp->hciEventReceived(*packet);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback hciEventReceived failed");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);}
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_POST_STACK_EVT_CALL_BACK);
#endif}break;case HCI_PACKET_TYPE_ACL_DATA:{
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_PRE_STACK_ACL_CALL_BACK);
#endifauto hidl_client_status = event_cb_tmp->aclDataReceived(*packet);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback aclDataReceived failed");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);}
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_POST_STACK_ACL_CALL_BACK);
#endif}break;default:ALOGE("%s Unexpected event type %d", __func__, type);break;}});if (!rc && (cb != nullptr)) {...} else if (rc && (cb != nullptr)) {ALOGI("%s: linking to deathRecipient", __func__);cb->linkToDeath(deathRecipient, 0);}return Void();
}
// BluetoothHci::initialize 函數調用開始
01-13 02:03:26.451268   714   714 W vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: BluetoothHci::initialize(), slot001-13 02:03:26.451326   714   714 W vendor.RunningAndroid.bluetooth@1.0-data_handler: DataHandler:: Init()
01-13 02:03:26.451454   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: data_service_setup_sighandler: Entry
01-13 02:03:26.451487   714   714 D vendor.RunningAndroid.bluetooth@1.0-data_handler: isProtocolAdded:
01-13 02:03:26.451498   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: isProtocolAdded: status:0
01-13 02:03:26.451507   714   714 I vendor.RunningAndroid.bluetooth@1.0-data_handler: Open init_status 0 
01-13 02:03:26.451897   714   714 D vendor.RunningAndroid.bluetooth@1.0-wake_lock: Init wakelock is initiated // BluetoothHci::initialize 函數調用退出
01-13 02:03:26.452016   714   714 I vendor.RunningAndroid.bluetooth@1.0-bluetooth_hci: initialize: linking to deathRecipient

在 BluetoothHci::initialize 中最主要就是調用 DataHandler::Init

1. DataHandler::Init

// hidl_hci/1.0/default/data_handler.cpp
bool DataHandler::Init(ProtocolType type, InitializeCallback init_cb,DataReadCallback data_read_cb)
{// lock required incase of multiple binder threadsALOGW("DataHandler:: Init()");std::unique_lock<std::mutex> guard(init_mutex_);Get();// 將上述兩個回調 傳入 他的 Open 函數return data_handler->Open(type, init_cb, data_read_cb);
}

DataHandler::Init 有兩個回調 :

  1. InitializeCallback init_cb

  2. DataReadCallback data_read_cb

1. InitializeCallback init_cb

其中 InitializeCallback init_cb 傳入的內容是:

[this, event_cb_tmp](bool status) {if (event_cb_tmp != nullptr) {ALOGI("%s: Set callbacks received from BT client inorder ""to provide status and data through them", __func__);event_cb_ = event_cb_tmp;}if (event_cb_ != nullptr) {auto hidl_client_status = event_cb_->initializationComplete(status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback initializationComplete failed");}}}
2. DataReadCallback data_read_cb

DataReadCallback data_read_cb 傳入的內容是:

 [this, event_cb_tmp](HciPacketType type, const hidl_vec<uint8_t> *packet) {DataHandler *data_handler = DataHandler::Get();if (event_cb_tmp == nullptr) {ALOGE("BluetoothHci: event_cb_tmp is null");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);return;}/* Skip calling client callback when client is dead */if(data_handler && (data_handler->GetClientStatus(TYPE_BT) == false)) {ALOGI("%s: Skip calling client callback when client is dead", __func__);return;}Logger::Get()->UpdateRxTimeStamp();switch (type) {case HCI_PACKET_TYPE_EVENT:{
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_PRE_STACK_EVT_CALL_BACK);
#endifauto hidl_client_status = event_cb_tmp->hciEventReceived(*packet);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback hciEventReceived failed");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);}
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_POST_STACK_EVT_CALL_BACK);
#endif}break;case HCI_PACKET_TYPE_ACL_DATA:{
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_PRE_STACK_ACL_CALL_BACK);
#endifauto hidl_client_status = event_cb_tmp->aclDataReceived(*packet);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback aclDataReceived failed");if (data_handler)data_handler->SetClientStatus(false, TYPE_BT);}
#ifdef DUMP_RINGBUF_LOGLogger::Get()->UpdateRxEventTag(RX_POST_STACK_ACL_CALL_BACK);
#endif}break;default:ALOGE("%s Unexpected event type %d", __func__, type);break;}}

將上述兩個回調 傳入 他的 Open 函數

2. DataHandler::Open

bool DataHandler::Open(ProtocolType type, InitializeCallback init_cb,DataReadCallback data_read_cb)
{char dst_buff[MAX_BUFF_SIZE];char init_buff[MAX_BUFF_SIZE];struct timeval tv;std::map<ProtocolType, ProtocolCallbacksType *>::iterator it;std::unique_lock<std::mutex> guard(internal_mutex_);ALOGI("Open init_status %d \n", init_status_);// update the pending Init cb and other callbacksit = protocol_info_.find(type);if (it == protocol_info_.end()) {ProtocolCallbacksType *cb_data  = new (ProtocolCallbacksType);cb_data->type = type;cb_data->is_pending_init_cb = true;cb_data->init_cb = init_cb; // 將我們回調保存在 cb_data 中cb_data->data_read_cb = data_read_cb;protocol_info_[type] = cb_data;}switch (init_status_) { // 這里默認的 狀態是 0: INIT_STATUS_IDLE...case INIT_STATUS_IDLE:init_status_ = INIT_STATUS_INITIALIZING;break;}// 在這里 創建了一個 線程init_thread_ = std::thread(...);return true;
}

也就是說 bt.server 調用 initialize 函數后,hal 側啟動了一個線程 專門干這個事情。 initialize 函數就順利返回了。

主要 initialize 的工作全部在 該線程中處理了。

3. 轉門的線程處理 initialize 工作

線程處理函數如下:

[this, type]() {// 1. 啟動定時器StartInitTimer();if (!IsSocAlwaysOnEnabled()) {soc_need_reload_patch = true;}ALOGI("%s: soc_need_reload_patch = %d", __func__, soc_need_reload_patch); // 這里是1 if (soc_type_ == BT_SOC_SMD) {} else {// 2. 創建 UartController 對象controller_ = static_cast<Controller *> (new UartController(soc_type_));}if (controller_) {int retry_count = 0;// 進入循環 初始化。如果初始化失敗,接著嘗試while (retry_count < INIT_MAX_RETRY_TIMES) {// 3. 調用 controller_->Init 函數status = controller_->Init([this](ProtocolType ptype, HciPacketType type,const hidl_vec<uint8_t> *hidl_data)   {OnPacketReady(ptype, type, hidl_data);});if (status)break;++retry_count;}}...// 4. 初始化成功后 停止定時器StopInitTimer();std::unique_lock<std::mutex> guard(internal_mutex_);if (status) {/* Stop moving further if timeout detected */{guard.unlock();std::unique_lock<std::mutex> lock(DataHandler::init_timer_mutex_);if (GetInitTimerState() == TIMER_OVERFLOW) {ALOGW("Initialization timeout detected cleanup is in process");// Init thread exited.is_init_thread_killed = true;return;}guard.lock();init_status_ = INIT_STATUS_SUCCESS;ALOGD("Firmware download succeded.");}} else {...}std::map<ProtocolType, ProtocolCallbacksType *>::iterator it;for (auto& it: protocol_info_) {ProtocolCallbacksType *cb_data = (ProtocolCallbacksType*)it.second;cb_data->is_pending_init_cb = false;gettimeofday(&tv, NULL);snprintf(dst_buff, sizeof(dst_buff), "Init callback status = %d", status);BtState::Get()->AddLogTag(cb_status_buf, tv, dst_buff);BtState::Get()->SetTsStatusOfCbSent(cb_status_buf);// 5. 調用回調cb_data->init_cb(status); }// clear the list if the controller open call failsif (!status) {...}guard.unlock();// BT ON successfulproperty_set("persist.vendor.service.bdroid.system_delay_crash_count", "0");// Init thread exited.is_init_thread_killed = true;ALOGD("%s: init thread exited now", __func__);}

這個線程 主要做如下幾件事情:

  1. 啟動定時器
  2. 創建 UartController 對象
  3. 調用 controller_->Init 函數
  4. 初始化成功后 停止定時器
  5. 調用回調 init_cb

1. 啟動定時器

啟動定時器的目的是, 為了 防止 Controller 交互時, 無響應。 這里不是重點。暫時忽略。

2.創建 UartController 對象

// hidl_hci/1.0/default/uart_controller.cppUartController::UartController(BluetoothSocType soc_type): soc_crashed(false), soc_type_(soc_type),hci_packetizer_([this](hidl_vec<uint8_t> *data) { OnPacketReady(data); })
{
...
}

UartController 沒有啥可以講述的,一堆變量。暫時忽略

3. 調用 controller_->Init 函數


bool UartController::Init(PacketReadCallback pkt_read_cb)
{power_manager_.Init(soc_type_);// 1. 給芯片 上電if (soc_need_reload_patch) {// power off the chip firstpower_manager_.SetPower(false);// power on the chip using power managerpower_manager_.SetPower(true);}// 2. 初始化 HciTransporthci_transport_ = static_cast<HciTransport*> (uart_transport);ret = uart_transport->Init(soc_type_, soc_need_reload_patch);// 3. 創建 固件 補丁管理器patch_dl_manager = new (std::nothrow)PatchDLManager(soc_type_, uart_transport, &power_manager_);uart_transport->ClockOperation(USERIAL_OP_CLK_ON);//Download the NVM/RAM patchif (soc_need_reload_patch) {logger_->PropertyGet("vendor.wc_transport.skip_patch_dload", skip_patch_download, "false");if (strcmp(skip_patch_download, "true") != 0) {// 4. 開始打補丁, 下載固件if (patch_dl_manager->PerformChipInit() < 0) {}temp_add_on_features = patch_dl_manager->GetAddOnFeatureList();} else {}}// 獲取 controller 的芯片版本, 做記錄使用chipset_ver_ = patch_dl_manager->GetChipVersion();init_done_ = true;ALOGD("Init succeded");return init_done_;
}

由于篇幅原因,這里將于 下一篇,中講解 這部分內容

4. 初始化成功后 停止定時器

這里不是重點。暫時忽略。

5. 調用回調 init_cb

當芯片上完電, 已經下載完固件后, 將調用 回調。

cb_data->init_cb(status); 

這里將回調到 3.2.1.1 小節中的回調。

[this, event_cb_tmp](bool status) {if (event_cb_tmp != nullptr) {ALOGI("%s: Set callbacks received from BT client inorder ""to provide status and data through them", __func__);event_cb_ = event_cb_tmp;}if (event_cb_ != nullptr) {auto hidl_client_status = event_cb_->initializationComplete(status ? Status::SUCCESS : Status::INITIALIZATION_ERROR);if(!hidl_client_status.isOk()) {ALOGE("Client dead, callback initializationComplete failed");}}}
  • 最終會回調到 bt.server 中的 initializationComplete 函數。告知 bt.server 初始化成功。

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

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

相關文章

JavaEE 初階文件操作與 IO 詳解

一、文件操作基礎&#xff1a;File 類 作用&#xff1a;操作文件或目錄&#xff08;創建、刪除、獲取信息&#xff09;。 核心方法&#xff1a; exists()&#xff1a;文件是否存在createNewFile()&#xff1a;創建新文件mkdir()&#xff1a;創建目錄delete()&#xff1a;刪除…

C++(27): 標準庫 <iterator>

目錄 1. 核心概念 2. 基本語法 3. 特點 4. 特有成員函數與工具 5. 內存與性能 6. 示例代碼 7. 成員函數與類型 8. 使用場景 9. 注意事項 1. 核心概念 迭代器(Iterator) 是 C++ 中用于訪問和遍歷容器元素的通用接口,類似于指針,但支持更豐富的操作。 抽象訪問機制:…

談談 Kotlin 中的構造方法,有哪些注意事項?

在 Kotlin 中&#xff0c;構造方法分為主構造方法&#xff08;Primary Constructor&#xff09;和次構造方法&#xff08;Secondary Constructor&#xff09;。 1 主構造方法 主構造方法是類的核心構造方法&#xff0c;直接在類頭聲明&#xff0c;位于類名之后。 1.1 基本語…

年會招標抽獎活動軟件———仙盟創夢IDE

年會是企業一年的總結與歡慶時刻&#xff0c;而抽獎環節更是點燃全場氣氛的關鍵。如何讓抽獎環節既大氣又充滿儀式感&#xff1f;選對抽獎軟件至關重要&#xff01;本文精心挑選了 3 款兼具實用性與氛圍感的年會抽獎軟件&#xff0c;從界面設計到功能特色&#xff0c;全方位為你…

安全軟件檢測進程異常行為-Postgresql應用執行異常指令whoami

文章目錄 環境癥狀問題原因解決方案 環境 系統平臺&#xff1a;UOS&#xff08;海光&#xff09; 版本&#xff1a;4.5.8 癥狀 數據庫安裝包&#xff1a; 安全軟件告警中提示“sh -c whoami”命令&#xff0c;是由數據庫發出的&#xff0c;安全軟件捕獲到了postgres.exe–fo…

車載診斷架構 --- LIN 節點 ECU 故障設計原則

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

GPU加速的AlphaFold3蛋白質復合體預測:如何在64GB顯存下跑超大規模模型(混合精度+模型并行實戰技巧)

一、AlphaFold3的超大規模挑戰與優化方向 AlphaFold3作為當前生物計算領域的革命性工具&#xff0c;其核心架構基于擴散模型&#xff0c;能夠預測包含蛋白質、核酸、小分子配體等復雜生物復合物的三維結構。然而&#xff0c;模型參數量級&#xff08;典型配置超百億級&#xf…

Qt功能區:Ribbon控件

控件 1. 按鈕1.1 多選按鈕1.2 2. 下拉列表框SARibbonComboBox2.1 簡介2.2 代碼實現 1. 按鈕 1.1 多選按鈕 軟件功能&#xff1a;用于實現Category的名稱居中。 SARibbonCheckBox繼承于QCheckBox&#xff0c;使用方法完全相同。 SARibbonCheckBox* checkBox new SARibbonChe…

一個由微軟開源的 Python 工具,用于將多種文件格式轉換為 Markdown 格式

&#x1f4da; Markitdown 由微軟開源的 Python 工具&#xff0c;用于將多種文件格式轉換為 Markdown 格式 支持&#xff1a;PDF、PowerPoint、Word、Excel、圖像、音頻、HTML、文本格式&#xff08;CSV、JSON、XML&#xff09;、ZIP 文件的轉換。 它旨在提供一個簡單且靈活的…

Linux的進程概念

目錄 1、馮諾依曼體系結構 2、操作系統(Operating System) 2.1 基本概念 2.2 目的 3、Linux的進程 3.1 基本概念 3.1.1 PCB 3.1.2 struct task_struct 3.1.3 進程的定義 3.2 基本操作 3.2.1 查看進程 3.2.2 初識fork 3.3 進程狀態 3.3.1 操作系統的進程狀態 3.3…

export和import的書寫方式

一、導出模塊&#xff08;export&#xff09; 1. 命名導出&#xff08;Named Exports&#xff09; // math.js export const PI 3.14159; // 導出單個常量 export function sum(a, b) { return a b; } // 導出單個函數 export class Calculator { /* ..…

HOW - 結合 AI 進行 Tailwind 樣式開發

文章目錄 情況 1&#xff1a;使用 Tailwind CSS 與手寫傳統 CSS 的開發效率對比情況 2&#xff1a;AI Tailwind 自動生成 UI 的效率如何&#xff1f;總結 在 WHAT - Tailwind 樣式方案&#xff08;不寫任何自定義樣式&#xff09; 中我們已經簡單介紹過 Tailwind。今天主要認識…

java面試每日一背 day1

1.什么是緩存穿透 緩存穿透是指查詢一個數據庫中根本不存在的數據&#xff0c;導致這個查詢請求繞過緩存直接訪問數據庫的情況。這種情況如果頻繁發生&#xff0c;會對數據庫造成不必要的壓力。 典型特征&#xff1a; &#xff08;1&#xff09;查詢的數據在數據庫和緩存中都…

ngx_http_realip_module 模塊概述

一、使用場景 日志記錄 記錄真實客戶端 IP 而非反向代理的 IP&#xff0c;有助于流量分析和安全審計。訪問控制 基于真實 IP 實現防火墻規則&#xff08;allow/deny&#xff09;或限流&#xff0c;而非誤將上游 IP 視為客戶端。GeoIP、WAF、限速等功能 模塊化的上游真實 IP 支…

實戰5:個性化數字藝術生成與銷售

盈利思路 數字藝術銷售&#xff1a; 平臺銷售&#xff1a;將生成的數字藝術作品上傳到像OpenSea、Foundation等NFT平臺進行售賣。每一件獨特的藝術品可以通過NFT技術保證其唯一性&#xff0c;吸引收藏家和投資者。 定價策略&#xff1a;根據作品的復雜度、創意性以及市場需求來…

游戲引擎學習第303天:嘗試分開對Y軸和Z軸進行排序

成為我們自己的代碼精靈α 所以現在應該可以正常使用了。不過&#xff0c;這兩周我們沒辦法繼續處理代碼里的問題&#xff0c;而之前留在代碼里的那個問題依然存在&#xff0c;沒有人神奇地幫我們修復&#xff0c;這讓人挺無奈的。其實我們都希望有個神奇的“代碼仙子”&#…

InetAddress 類詳解

InetAddress 類詳解 一、核心作用 封裝 IP 地址&#xff1a;同時支持 IPv4 和 IPv6 地址域名解析&#xff1a;將域名轉換為 IP 地址&#xff08;DNS 查詢&#xff09;地址驗證&#xff1a;檢查網絡地址的有效性無構造方法&#xff1a;通過靜態工廠方法獲取實例 二、核心方法 …

spring cloud alibaba-Geteway詳解

spring cloud alibaba-Gateway詳解 Gateway介紹 在 Spring Cloud Alibaba 生態系統中&#xff0c;Gateway 是一個非常重要的組件&#xff0c;用于構建微服務架構中的網關服務。它基于 Spring Cloud Gateway 進行擴展和優化&#xff0c;提供了更強大的功能和更好的性能。 Gat…

iOS 直播技術及優化

iOS直播技術的實現和優化涉及多個技術環節&#xff0c;需結合協議選擇、編解碼方案、播放器技術及性能調優等多方面。 一、核心技術實現 協議選擇與傳輸優化 HLS&#xff08;HTTP Live Streaming&#xff09;&#xff1a;蘋果官方推薦&#xff0c;基于HTTP分片傳輸&#xff0c…

目標檢測135個前沿算法模型匯總(附源碼)!

目標檢測是計算機視覺核心方向之一&#xff0c;也是發論文的熱門領域&#xff01; 近來不僅YOLO算法迎來了新突破&#xff0c;迭代出YOLOv12&#xff01;Mamba、大模型等新技術的發展&#xff0c;也給該領域注入了全新的力量&#xff0c;取得了諸多顯著成果。比如性能飆升82.3…