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

OpenHarmony 3GPP协议开发深度剖析——一文读懂RIL

市面上关于终端(手机)操作系统在 3GPP 协议开发的内容太少了,即使 Android 相关的学习文档都很少,Android 协议开发书籍我是没有见过的。可能是市场需求的缘故吧,现在市场上还是前后端软件开发从业人员最多,包括我自己。

基于我曾经也在某手机协议开发团队干过一段时间,协议的 AP 侧和 CP 侧开发都整过,于是想尝试下基于 OpenAtom OpenHarmony(以下简称“OpenHarmony”)源码写点内容,帮助大家了解下协议开发领域,尽可能将 3gpp 协议内容与 OpenHarmony 电话子系统模块进行结合讲解。据我所知,现在终端协议开发非常缺人。首先声明我不是协议专家,我也离开该领域有五六年了,如有错误,欢迎指正。

提到终端协议开发,我首先想到的就是** RIL **了。

专有名词

CP: Communication Processor(通信处理器),我一般就简单理解为 modem 侧,也可以理解为底层协议,这部分由各个 modem 芯片厂商完成(比如海思、高通)。

AP: Application Processor(应用处理器),通常就是指的手机终端,我一般就简单理解为上层协议,主要由操作系统 Telephony 服务来进行处理。

RIL: Radio Interface Layer(无线电接口层),我一般就简单理解为硬件抽象层,即 AP 侧将通信请求传给 CP 侧的中间层。

AT指令: AT 指令是应用于终端设备与 PC 应用之间的连接与通信的指令。

设计思想

常规的 Modem 开发与调试可以使用 AT 指令来进行操作,而各家的 Modem 芯片的 AT 指令都会有各自的差异。因此手机终端厂商为了能在各种不同型号的产品中集成不同 modem 芯片,需要进行解耦设计来屏蔽各家 AT 指令的差异。

于是 OpenHarmony 采用 RIL 对 Modem 进行 HAL(硬件抽象),作为系统与 Modem 之间的通信桥梁,为 AP 侧提供控制 Modem 的接口,各 Modem 厂商则负责提供对应于 AT 命令的 Vender RIL(这些一般为封装好的 so 库),从而实现操作系统与 Modem 间的解耦。

OpenHarmony RIL架构

**框架层:**Telephony Service,电话子系统核心服务模块,主要功能是初始化 RIL 管理、SIM 卡和搜网模块。对应 OpenHarmony 的源码仓库 OpenHarmony / telephony_core_service。这个模块也是非常重要的一个模块,后期单独再做详细解读。

**硬件抽象层:**即我们要讲的 RIL,对应 OpenHarmony 的源码仓库 OpenHarmony / telephony_ril_adapter。RIL Adapter 模块主要包括厂商库加载,业务接口实现以及事件调度管理。主要用于屏蔽不同 modem 厂商硬件差异,为上层提供统一的接口,通过注册 HDF 服务与上层接口通讯。

**芯片层:**Modem 芯片相关代码,即 CP 侧,这些代码各个 Modem 厂商是不开放的,不出现在 OpenHarmony 中。

硬件抽象层

硬件抽象层又被划分为了 hril_hdf 层、hril 层和 venderlib 层。

**hril_hdf层:**HDF 服务,基于 OpenHarmony HDF 框架,提供 hril 层与 Telephony Service 层进行通讯。

**hril 层:**hril 层的各个业务模块接口实现,比如通话、短彩信、数据业务等。

**vendorlib层:**各 Modem 厂商提供的对应于 AT 命令库,各个厂商可以出于代码闭源政策,在这里以 so 库形式提供。目前源码仓中已经提供了一套提供代码的 AT 命令操作,至于这个是针对哪个型号 modem 芯片的,我后续了解清楚再补充。

下面是 ril_adapter 仓的源码结构:

base/telephony/ril_adapter
├── figures                             # readme资源文件
├── frameworks
│   ├── BUILD.gn
│   └── src                             # 序列化文件
├── interfaces                          # 对应提供上层各业务内部接口
│   └── innerkits
├── services                            # 服务
│   ├── hril                            # hril层的各个业务模块接口实现
│   ├── hril_hdf                        # HDF服务
│   └── vendor                          # 厂商库文件
└── test                                # 测试代码├── BUILD.gn├── mock└── unittest                        # 单元测试代码

核心业务逻辑梳理

本文解读 RIL 层很小一部分代码,RIL 是如何通过 HDF 与 Telephony 连接上的,以后更加完整的逻辑梳理会配上时序图讲解,会更加清晰。首先我们要对 OpenHarmony 的 HDF(Hardware Driver Foundation)驱动框架做一定了解,最好是动手写一个 Demo 案例,具体的可以单独去官网查阅 HDF 资料。

首先,找到 hril_hdf.c 文件的代码,它承担的是驱动业务部分,源码中是不带中文注释的,为了梳理清楚流程,我给源码关键部分加上了中文注释。

/** Copyright (C) 2021 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#include "hril_hdf.h"#include <stdlib.h>
#include <libudev.h>
#include <pthread.h>#include "dfx_signal_handler.h"
#include "parameter.h"#include "modem_adapter.h"
#include "telephony_log_c.h"#define RIL_VENDOR_LIB_PATH "persist.sys.radio.vendorlib.path"
#define BASE_HEX 16static struct HRilReport g_reportOps = {OnCallReport,OnDataReport,OnModemReport,OnNetworkReport,OnSimReport,OnSmsReport,OnTimerCallback
};static int32_t GetVendorLibPath(char *path)
{int32_t code = GetParameter(RIL_VENDOR_LIB_PATH, "", path, PARAMETER_SIZE);if (code <= 0) {TELEPHONY_LOGE("Failed to get vendor library path through system properties. err:%{public}d", code);return HDF_FAILURE;}return HDF_SUCCESS;
}static UsbDeviceInfo *GetPresetInformation(const char *vId, const char *pId)
{char *out = NULL;UsbDeviceInfo *uDevInfo = NULL;int32_t idVendor = (int32_t)strtol(vId, &out, BASE_HEX);int32_t idProduct = (int32_t)strtol(pId, &out, BASE_HEX);for (uint32_t i = 0; i < sizeof(g_usbModemVendorInfo) / sizeof(UsbDeviceInfo); i++) {if (g_usbModemVendorInfo[i].idVendor == idVendor && g_usbModemVendorInfo[i].idProduct == idProduct) {TELEPHONY_LOGI("list index:%{public}d", i);uDevInfo = &g_usbModemVendorInfo[i];break;}}return uDevInfo;
}static UsbDeviceInfo *GetUsbDeviceInfo(void)
{struct udev *udev;struct udev_enumerate *enumerate;struct udev_list_entry *devices, *dev_list_entry;struct udev_device *dev;UsbDeviceInfo *uDevInfo = NULL;udev = udev_new();if (udev == NULL) {TELEPHONY_LOGE("Can't create udev");return uDevInfo;}enumerate = udev_enumerate_new(udev);if (enumerate == NULL) {TELEPHONY_LOGE("Can't create enumerate");return uDevInfo;}udev_enumerate_add_match_subsystem(enumerate, "tty");udev_enumerate_scan_devices(enumerate);devices = udev_enumerate_get_list_entry(enumerate);udev_list_entry_foreach(dev_list_entry, devices) {const char *path = udev_list_entry_get_name(dev_list_entry);if (path == NULL) {continue;}dev = udev_device_new_from_syspath(udev, path);if (dev == NULL) {continue;}dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");if (!dev) {TELEPHONY_LOGE("Unable to find parent usb device.");return uDevInfo;}const char *cIdVendor = udev_device_get_sysattr_value(dev, "idVendor");const char *cIdProduct = udev_device_get_sysattr_value(dev, "idProduct");uDevInfo = GetPresetInformation(cIdVendor, cIdProduct);udev_device_unref(dev);if (uDevInfo != NULL) {break;}}udev_enumerate_unref(enumerate);udev_unref(udev);return uDevInfo;
}static void LoadVendor(void)
{const char *rilLibPath = NULL;char vendorLibPath[PARAMETER_SIZE] = {0};// Pointer to ril init function in vendor rilconst HRilOps *(*rilInitOps)(const struct HRilReport *) = NULL;// functions returned by ril init function in vendor rilconst HRilOps *ops = NULL;UsbDeviceInfo *uDevInfo = GetUsbDeviceInfo();if (GetVendorLibPath(vendorLibPath) == HDF_SUCCESS) {rilLibPath = vendorLibPath;} else if (uDevInfo != NULL) {rilLibPath = uDevInfo->libPath;} else {TELEPHONY_LOGI("use default vendor lib.");rilLibPath = g_usbModemVendorInfo[DEFAULT_MODE_INDEX].libPath;}if (rilLibPath == NULL) {TELEPHONY_LOGE("dynamic library path is empty");return;}TELEPHONY_LOGI("RilInit LoadVendor start with rilLibPath:%{public}s", rilLibPath);g_dlHandle = dlopen(rilLibPath, RTLD_NOW);if (g_dlHandle == NULL) {TELEPHONY_LOGE("dlopen %{public}s is fail. %{public}s", rilLibPath, dlerror());return;}rilInitOps = (const HRilOps *(*)(const struct HRilReport *))dlsym(g_dlHandle, "RilInitOps");if (rilInitOps == NULL) {dlclose(g_dlHandle);TELEPHONY_LOGE("RilInit not defined or exported");return;}ops = rilInitOps(&g_reportOps);HRilRegOps(ops);TELEPHONY_LOGI("HRilRegOps completed");
}// 用来处理用户态发下来的消息
static int32_t RilAdapterDispatch(struct HdfDeviceIoClient *client, int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{int32_t ret;static pthread_mutex_t dispatchMutex = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock(&dispatchMutex);TELEPHONY_LOGI("RilAdapterDispatch cmd:%{public}d", cmd);ret = DispatchRequest(cmd, data);pthread_mutex_unlock(&dispatchMutex);return ret;
}static struct IDeviceIoService g_rilAdapterService = {.Dispatch = RilAdapterDispatch,.Open = NULL,.Release = NULL,
};//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架
static int32_t RilAdapterBind(struct HdfDeviceObject *device)
{if (device == NULL) {return HDF_ERR_INVALID_OBJECT;}device->service = &g_rilAdapterService;return HDF_SUCCESS;
}// 驱动自身业务初始的接口
static int32_t RilAdapterInit(struct HdfDeviceObject *device)
{if (device == NULL) {return HDF_ERR_INVALID_OBJECT;}DFX_InstallSignalHandler();struct HdfSBuf *sbuf = HdfSbufTypedObtain(SBUF_IPC);if (sbuf == NULL) {TELEPHONY_LOGE("HdfSampleDriverBind, failed to obtain ipc sbuf");return HDF_ERR_INVALID_OBJECT;}if (!HdfSbufWriteString(sbuf, "string")) {TELEPHONY_LOGE("HdfSampleDriverBind, failed to write string to ipc sbuf");HdfSbufRecycle(sbuf);return HDF_FAILURE;}if (sbuf != NULL) {HdfSbufRecycle(sbuf);}TELEPHONY_LOGI("sbuf IPC obtain success!");LoadVendor();return HDF_SUCCESS;
}// 驱动资源释放的接口
static void RilAdapterRelease(struct HdfDeviceObject *device)
{if (device == NULL) {return;}dlclose(g_dlHandle);
}//驱动入口注册到HDF框架,这里配置的moduleName是找到Telephony模块与RIL进行通信的一个关键配置
struct HdfDriverEntry g_rilAdapterDevEntry = {.moduleVersion = 1,.moduleName = "hril_hdf",.Bind = RilAdapterBind,.Init = RilAdapterInit,.Release = RilAdapterRelease,
};
// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
HDF_INIT(g_rilAdapterDevEntry);

上述代码中配置了对应该驱动的 moduleName 为"hril_hdf",因此我们需要去找到对应驱动的配置文件,以 Hi3516DV300 开发板为例,它的驱动配置在 vendor_hisilicon/ Hi3516DV300 / hdf_config / uhdf / device_info.hcs 代码中可以找到,如下:

riladapter :: host {hostName = "riladapter_host";priority = 50;riladapter_device :: device {device0 :: deviceNode {policy = 2;priority = 100;moduleName = "libhril_hdf.z.so";serviceName = "cellular_radio1";}}}

这里可以发现该驱动对应的服务名称为 cellular_radio1,那么 telephony_core_service 通过 HDF 与 RIL 进行通信肯定会调用到该服务名称,因此无查找 telephony_core_service 的相关代码,可以很快定位到 telephony_core_service/ services / tel_ril / src / tel_ril_manager.cpp 该代码,改代码中有一个关键类 TelRilManager,它用来负责管理 tel_ril。

看 tel_ril_manager.cpp 中的一个关键函数 ConnectRilAdapterService,它就是用来通过 HDF 框架获取RIL_ADAPTER 的服务,之前定义过 RIL_ADAPTER_SERVICE_NAME 常量为 “cellular_radio1”,它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs 中配置的 hril_hdf 驱动对应的服务名称。

bool TelRilManager::ConnectRilAdapterService()
{std::lock_guard<std::mutex> lock_l(mutex_);rilAdapterRemoteObj_ = nullptr;auto servMgr_ = OHOS::HDI::ServiceManager::V1_0::IServiceManager::Get();if (servMgr_ == nullptr) {TELEPHONY_LOGI("Get service manager error!");return false;}//通过HDF框架获取RIL_ADAPTER的服务,之前定义过RIL_ADAPTER_SERVICE_NAME常量为"cellular_radio1",它就是在 vendor_hisilicon/ XXXX / hdf_config / uhdf / device_info.hcs中配置的hril_hdf驱动对应的服务名称 rilAdapterRemoteObj_ = servMgr_->GetService(RIL_ADAPTER_SERVICE_NAME.c_str());if (rilAdapterRemoteObj_ == nullptr) {TELEPHONY_LOGE("bind hdf error!");return false;}if (death_ == nullptr) {TELEPHONY_LOGE("create HdfDeathRecipient object failed!");rilAdapterRemoteObj_ = nullptr;return false;}if (!rilAdapterRemoteObj_->AddDeathRecipient(death_)) {TELEPHONY_LOGE("AddDeathRecipient hdf failed!");rilAdapterRemoteObj_ = nullptr;return false;}int32_t ret = SetCellularRadioIndication();if (ret != CORE_SERVICE_SUCCESS) {TELEPHONY_LOGE("SetCellularRadioIndication error, ret:%{public}d", ret);return false;}ret = SetCellularRadioResponse();if (ret != CORE_SERVICE_SUCCESS) {TELEPHONY_LOGE("SetCellularRadioResponse error, ret:%{public}d", ret);return false;}return true;
}

为了帮助到大家能够更有效的学习OpenHarmony 开发的内容,下面特别准备了一些相关的参考学习资料:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

相关文章:

OpenHarmony 3GPP协议开发深度剖析——一文读懂RIL

市面上关于终端&#xff08;手机&#xff09;操作系统在 3GPP 协议开发的内容太少了&#xff0c;即使 Android 相关的学习文档都很少&#xff0c;Android 协议开发书籍我是没有见过的。可能是市场需求的缘故吧&#xff0c;现在市场上还是前后端软件开发从业人员最多&#xff0c…...

windows部署腾讯tmagic-editor02-Runtime

创建editor项目 将上一教程中的hello-world复制过来&#xff0c;改名hello-editor 创建runtime项目 和hello-editor同级 pnpm create vite删除src/components/HelloWorld.vue 按钮需要用的ts types依赖 pnpm add tmagic/schema tmagic/stage实现runtime 将hello-editor中…...

“分块”算法的基本要素及 build() 函数的构建细节

【“分块”算法知识点】 ● 分块是用线段树的分区思想改良的暴力法。代码比线段树简单。效率比普通暴力法高。分块适合求解 m=n=10^5 规模的问题,或 m*sqrt(n)≈10^7 的问题。其中,n 为元素个数,m 为操作次数。 ● “分块”算法的基本要素 (1)块的大小用 block 表示。通常…...

畅捷通TPlus keyEdit.aspx、KeyInfoList.aspx SQL注入漏洞复现

前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 一、产…...

Ubuntu22 下配置 Qt5 环境

1. Qt 简介 Qt5 中的新功能&#xff0c;可以看到各个版本的情况Whats New in Qt 5 | Qt 5.15 Qt 源文件网址Index of /archive/qt 2. 安装 Qt Creator cd 到安装包所在目录&#xff0c;进行软件安装。赋予可执行权限&#xff0c;加上 sudo 权限进入安装&#xff0c;这样会安…...

普通人也能创业!轻资产短视频带货项目,引领普通人实现创业梦想

在这个信息爆炸的时代&#xff0c;创业似乎成为了越来越多人的梦想。然而&#xff0c;传统的创业模式 keJ0277 往往伴随着高昂的资金投入和复杂的管理流程&#xff0c;让许多普通人望而却步。然而&#xff0c;现在有一种轻资产短视频带货项目正在悄然兴起&#xff0c;它以其低…...

【Maven】Nexus简单使用

1、安装配置介绍Nexus私服&#xff1a; 安装配置指路上一篇详细教程博客 【Maven】Nexus私服简介_下载安装_登录-CSDN博客 简单介绍原有仓库类型&#xff1a; proxy代理仓库&#xff1a;代理远程仓库&#xff0c;访问全球中央仓库或其他公共仓库&#xff0c;将资源存储在私…...

winform嵌入excel 设置父窗体分辨率不是100% 嵌入excel分辨率变成双倍大小

在WinForms应用程序中嵌入Excel时&#xff0c;遇到分辨率问题可能是由于DPI缩放导致的。Windows 10及更高版本默认启用了DPI缩放&#xff0c;以便在高分辨率显示器上显示更清晰的内容。这可能会导致嵌入的应用程序&#xff08;如Excel&#xff09;看起来变大或变小。 解决方案 …...

前端系列-4 promise与async/await与fetch/axios使用方式

背景&#xff1a; 本文介绍promise使用方式&#xff0c;以及以Promise为基础的async/await用法和fetch/axios使用方式&#xff0c;主要以案例的方式进行。 1.promise 1.1 promise介绍 javascript是单线程执行的&#xff0c;异步编程的本质是事件机制和函数回调。当执行阻塞…...

微信公众号自定义分销商城小程序源码系统 带完整的安装代码吧以及系统部署搭建教程

系统概述 微信公众号自定义分销商城小程序源码系统是一款功能强大的电商解决方案&#xff0c;它集成了商品管理、订单处理、支付接口、分销管理等多种功能。该系统支持自定义界面设计&#xff0c;商家可根据自身需求调整商城的页面布局和风格&#xff0c;打造独特的品牌形象。…...

在另外一个页面,让另外一个页面弹框显示操作(调佣公共的弹框)vue

大概意思是&#xff0c;登录弹框在另外一个页面中&#xff0c;而当前页面不存在&#xff0c;在当前页面中判断如果token不存在&#xff0c;就弹框出登录的弹框 最后一行 window.location.href … 如果当前用户已登录&#xff0c;则执行后续操作(注意此处&#xff0c;可不要)...

羊毛-百度Comate领50京东E卡

给你分享一个AI编码助手——百度Comate&#xff01;扫码参与抽红包活动&#xff0c;520宠粉&#xff01;送京东卡&#xff01;https://comate.baidu.com/?inviteCodeyysudp63 流程如下 点击&#xff1a;https://comate.baidu.com/?inviteCodeyysudp63添加链接描述 验证码…...

kafka安装部署

kafka 官网下载&#xff1a; kafka https://downloads.apache.org/kafka/3.7.0/zookeeper https://downloads.apache.org/zookeeper/ run kafkazookeeper&#xff0c;conf目录下创建zoo.cfg&#xff0c;运行bin目录下的zkServer脚本文件 kafka eagle 参考&#xff1a;htt…...

VBA直连SAP RFC 接口实例

引用依赖&#xff1a; VBA 调用 SAP API的RFC函数&#xff1a;RFC_READ_TABLE Sub A() 查询SAP表数据并输出到EXCEL&#xff0c;VBA中不区分大小写&#xff08;保存后会自动把代码、变量转换大小写&#xff09;Dim iData As Integer Dim nField As Integer Dim nData As Intege…...

2024如何挑选开放式蓝牙耳机?热门爆款熬夜整理六个点!

我以前也经常使用入耳式耳机&#xff0c;但总是会感觉耳机插在耳朵里不舒服&#xff0c;戴久了耳朵很疼&#xff0c;跑步的时候还总掉。还有在过马路的时候接电话、听音乐&#xff0c;几乎感知不到周围环境音&#xff0c;很不安全。而有了一款开放式蓝牙耳机后&#xff0c;就可…...

3D数据格式转换工具HOOPS Exchange在PLM系统中的5大应用优势

在当今竞争激烈的制造业环境中&#xff0c;产品生命周期管理&#xff08;PLM&#xff09;系统已成为企业提升设计效率、缩短产品上市时间、降低成本和提高市场响应速度的关键工具。3D数据格式转换工具HOOPS Exchange&#xff0c;在PLM系统中扮演着至关重要的角色。以下是HOOPS …...

友元是一种允许某些外部函数或类访问另一个类的成员的机制

在C编程语言中&#xff0c;“友元”&#xff08;Friend&#xff09;是一种允许某些外部函数或类访问另一个类的私有&#xff08;private&#xff09;和保护&#xff08;protected&#xff09;成员的机制。友元功能在C中是非常有用的&#xff0c;尤其是在实现某些特定的功能时&a…...

儿童护眼台灯哪个牌子好,适合儿童使用的护眼台灯推荐

护眼台灯在近几年成为家长和经常与电子设备打交道的人士中备受瞩目的家用电器。对于有孩子的家庭而言&#xff0c;它几乎成为了必备品&#xff0c;许多消费者已经对其有了一定的了解并进行了购买。然而&#xff0c;仍有部分家长对护眼台灯的效果和重要性缺乏充分认识&#xff0…...

在电脑本地运行llama3-8b模型

文章目录 流程我的案例api调用llama.cpp 流程 ollama支持可运行的模型,图片这里只是一部分而已,只需要下载下面的软件和模型文件,即可直接运行,而无需配置其他 模型文件下载地址 https://ollama.com/library 支持的部分模型,实际上更多,这里只是显示部分 登陆ollama官网 htt…...

深入理解 House of Cat

Index 序言利用 FSOP 调用 House of Cat利用条件伪造IO流条件完整调用链分析 模板System (one_gadget) 模板ORW模板 Demo & Exp利用 __malloc_assert 调用 House of Cat例题&#xff1a;题目思路Exp 序言 原文章&#xff1a;深入理解 House of Cat 随着 GNU 持续不断的更…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

C++ 类基础:封装、继承、多态与多线程模板实现

前言 C 是一门强大的面向对象编程语言&#xff0c;而类&#xff08;Class&#xff09;作为其核心特性之一&#xff0c;是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性&#xff0c;包括封装、继承和多态&#xff0c;同时讨论类中的权限控制&#xff0c;并展示如何使用类…...

2025-06-08-深度学习网络介绍(语义分割,实例分割,目标检测)

深度学习网络介绍(语义分割,实例分割,目标检测) 前言 在开始这篇文章之前&#xff0c;我们得首先弄明白&#xff0c;什么是图像分割&#xff1f; 我们知道一个图像只不过是许多像素的集合。图像分割分类是对图像中属于特定类别的像素进行分类的过程&#xff0c;即像素级别的…...

AGV|无人叉车工业语音播报器|预警提示器LBE-LEX系列性能与接线说明

LBE-LEX系列AGV|无人叉车工业语音播报器|预警提示器&#xff0c;涵盖LBE-LEI-M-00、LBE-LESM-00、LBE-LES-M-01、LBE-LEC-M-00、LBE-KEI-M-00、LBE-KES-M-00、LBE-KES-M-01、LBE-KEC-M-00等型号&#xff0c;适用于各种需要语音提示的场景&#xff0c;主要有AGV、AMR机器人、无人…...

黑马Javaweb Request和Response

一.介绍 在 Web 开发中&#xff0c;HttpServletRequest 和 HttpServletResponse 是两个非常重要的类&#xff0c;它们分别用于处理客户端的请求和服务器的响应。以下是它们的详细说明和使用方法&#xff1a; 1. HttpServletRequest HttpServletRequest 是一个接口&#xff0…...

多线程语音识别工具

软件介绍 本文介绍一款支持大厂接口的语音转文字工具&#xff0c;具备免配置、免费使用的特点。 软件特性 该工具是一款完全免费的桌面端应用程序&#xff0c;部署于开源社区平台&#xff0c;其核心优势在于整合了多家技术供应商的接口资源。 操作方式 用户只需将音频…...