【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.mzph.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…