openharmony中hdf框架的驱动消息机制的实现原理
openharmony中hdf框架的驱动消息机制的实现原理
在分析hdf框架时发现绕来绕去的,整体梳理画了一遍流程图,发现还是有点模糊甚至不清楚如何使用的,详细的每个点都去剖析细节又过于消耗时间,所以有时间便从功能应用的角度一块块的去梳理。
此文为参考官方源码(oh5.0版本)中的docs\zh-cn\device-dev\driver\driver-hdf-manage.md驱动开发手册,将驱动消息机制这个小章节拿出来,单独做剖析的。官方手册中只涉及了如何使用,未涉及具体的原理。本文会先整体说下实现原理,然后结合驱动开发手册将涉及使用hdf接口函数的部分再往下剖了一下,目的是了解具体的实现逻辑。
概述
HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息,用于当用户态应用和内核态驱动需要交互的场景。
使用分析
由原理可知消息机制的功能主要有以下两种:
- 用户态应用发送消息到驱动。
- 用户态应用接收驱动主动上报事件。
表2 消息机制接口
| 方法 | 描述 |
|---|---|
| struct HdfIoService *HdfIoServiceBind(const char *serviceName); | 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。 |
| void HdfIoServiceRecycle(struct HdfIoService *service); | 用户态释放驱动的服务,与HdfIoServiceBind对应 |
| int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | 用户态程序注册接收驱动上报事件的操作方法。 |
| int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data) | 驱动主动上报事件接口。当驱动服务调用此函数发送消息时,所有通过 HdfDeviceRegisterEventListener注册了监听器的用户级应用程序都将收到该消息。 |
HdfIoServiceBind
用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息(见示例),函数的声明在接口文件中hdf_core\interfaces\inner_api\core\hdf_io_service_if.h,驱动开发者可以直接包含调用。
struct HdfIoService *HdfIoServiceBind(const char *serviceName)|-->HdfIoServiceAdapterObtain(serviceName) //获取设备服务接口的适配器函数|-->svcMgr = DevSvcManagerClntGetInstance();//获取设备服务管理实例|-->HdfDeviceObject *deviceObject = svcMgr->devSvcMgrIf->GetObject(svcMgr->devSvcMgrIf, serviceName);//通过设备服务管理器获取设备对象|-->DevSvcManagerGetObject //通过获取设备服务管理实例的创建过程可知上述函数的回调为此函数|-->serviceRecord = DevSvcManagerSearchServiceLocked(inst, serviceKey)//从设备服务管理器中搜索该名称的服务|-->return serviceRecord->value //返回设备对象|-->HdfIoServiceKClient *kClient = HdfHdfIoServiceKClientInstance(deviceObject)//通过设备对象获取设备服务的客户端实例|-->kDispatcher = {.Dispatch = HdfKIoServiceDispatch,};|-->struct HdfIoServiceKClient *client = OsalMemCalloc(sizeof(struct HdfIoServiceKClient))//分配内存|-->if (deviceObject->service->Open(&client->client) != HDF_SUCCESS)//回调驱动层的open函数|-->client->ioService.dispatcher = &kDispatcher//绑定具体的接口,|-->return &kClient->ioService//返回设备服务接口
HdfIoServiceRecycle
用户态释放驱动的服务,与HdfIoServiceBind对应,声明在接口文件中hdf_core\interfaces\inner_api\core\hdf_io_service_if.h,驱动开发者可以直接包含调用。
void HdfIoServiceRecycle(struct HdfIoService *service)|-->HdfIoServiceAdapterRecycle(service)|-->HdfIoServiceKClient *kClient = CONTAINER_OF(ioService, struct HdfIoServiceKClient, ioService)//根据ioService反推HdfIoServiceKClient对象①|-->kClient->client.device->service->Release(&kClient->client)//客户端设备的释放②|-->OsalMemFree(kClient)//对应HdfIoServiceBind函数中的HdfHdfIoServiceKClientInstance
①:关于反推函数CONTAINER_OF需要详细的了解的可以参考这篇文章
②:客户端设备的释放是在设备构建过程中构建的io服务的设备接口(IDeviceIoService),从整体的流程图中方便看,但太大了没法放上来,后续有时间整理了这部分再重新放上链接。✒️
HdfDeviceRegisterEventListener
用户态程序注册接收驱动上报事件的操作方法
int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener)|-->return HdfDeviceRegisterEventListenerWithSchedPolicy(target, listener, SCHED_OTHER)//为设备服务注册事件监听器,并指定事件处理线程的调度策略|-->struct HdfSyscallAdapter *adapter = CONTAINER_OF(target, struct HdfSyscallAdapter, super)//从HdfIoService结构体中获取其所属的HdfSyscallAdapter实例|-->if (!AddListenerToAdapterLocked(adapter, listener)) //将事件监听器添加到适配器中|--> ret = HdfIoServiceGroupThreadStart(adapter->group, policy) //如果适配器属于一个服务组(adapter->group不为空)便启动服务组的线程,并指定调度策略(policy即传入的SCHED_OTHER)|--> if (HdfIoServiceGroupThreadInit(group) != HDF_SUCCESS) //对线程的初始化|-->HdfDevListenerThreadDoInit(thread) |-->int32_t ret = OsalThreadCreate(&thread->thread, HdfDevEventListenTask, thread)//创建监听线程|-->//....路径较多省略了|-->int32_t HdfDevEventDispatchLocked(const struct HdfDevListenerThread *thread, struct HdfSyscallAdapter *adapter, const struct HdfWriteReadBuf *bwr)|-->(void)listener->callBack(listener->priv, bwr->cmdCode, sbuf)//此处进行监听事件的回调①|--> int32_t ret = HdfDevListenerThreadStart(group->thread)//初始化设备监听线程,并启动一个线程来处理设备事件|-->int32_t ret = HdfListenThreadInitPollFds(thread)//初始化监听的文件描述符列表|-->if (HdfAdapterStartListenIoctl(thread->pfds[i].fd)) {//启动监听|-->if (OsalThreadStart(&thread->thread, &config) != HDF_SUCCESS) {//启动线程return ret;|-->if (HdfIoServiceStartListen(adapter, policy) != HDF_SUCCESS)//如果适配器不属于服务组,启动事件处理线程,并指定调度策略|-->return HdfDevListenerThreadStart(adapter->thread)//初始化设备监听线程,并启动一个线程来处理设备事件
-
SCHED_OTHER是linux系统中默认的进程调度策略,想详细了解进程调度策略可以参考这篇文章
-
在本文后续的使用示例中可见此函数的使用的目的,主要是为了将事件进行回调,其中回调部分可参看本段代码的①处。
static struct HdfDevEventlistener listener = {.callBack = OnDevEventReceived,.priv ="Service0"
};
if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {HDF_LOGE("fail to register event listener");return HDF_FAILURE;
}
- 在此段代码中还可见有文件描述符的处理,即应用中我们用的select
、poll或epoll等。
HdfDeviceSendEvent
驱动主动上报事件接口,驱动服务调用此函数发送消息时,所有通过 HdfDeviceRegisterEventListener注册了监听器的用户级应用程序都将收到该消息。声明的头文件在hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h,意味着用户态可以直接包含调用。
int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data)|-->struct HdfDeviceNode *deviceNode = CONTAINER_OF(deviceObject, struct HdfDeviceNode, deviceObject)|-->adapter = (struct HdfVNodeAdapter *)(((struct DeviceNodeExt *)deviceNode)->ioService)|-->return HdfVNodeAdapterSendDevEvent(adapter, NULL, id, data)|-->ret = VNodeAdapterSendDevEventToClient(client, id, data)//将设备事件从驱动程序发送到客户端(通常是用户态应用程序或服务)|-->event = OsalMemAlloc(sizeof(struct HdfDevEvent));//分配事件对象|--> event->data = HdfSbufCopy(data);//给事件对象赋值|-->DListInsertTail(&event->listNode, &vnodeClient->eventQueue) //将事件对象插入到客户端的事件队列|-->wake_up_interruptible(&vnodeClient->pollWait)//唤醒等待事件的客户端线程
- 唤醒等待事件的客户端线程的wake_up_interruptible函数为linux内核的api函数,有需要详细了解的可以直接百度,想了解简单使用方法的可以参考这篇文章。
使用示例
驱动消息机制管理开发
-
将驱动配置信息(device_info.hcs)中服务策略policy字段设置为2(SERVICE_POLICY_CAPACITY,驱动对内核态和用户态都发布服务)。
device_sample :: Device {policy = 2;permission = 0644;... } -
配置驱动信息中的服务设备节点权限(permission字段)是框架给驱动创建设备节点的权限,默认是0666,驱动开发者根据驱动的实际使用场景配置驱动设备节点的权限。
权限值 含义 0666所有用户都可以读写该设备节点 0644所有者可以读写,所属组和其他用户可以读取 0640所有者可以读写,所属组可以读取,其他用户无法访问 0600只有所有者可以读写,其他用户无法访问 -
在服务实现过程中,实现服务基类成员IDeviceIoService中的Dispatch方法。
#include <hdf_log.h> #include <hdf_device_io.h> #include <hdf_device_desc.h> #include <hdf_sbuf.h>// 假设的其他服务函数 int32_t SampleDriverServiceA(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply) {HDF_LOGI("SampleDriverServiceA called");return HDF_SUCCESS; }int32_t SampleDriverServiceB(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply) {HDF_LOGI("SampleDriverServiceB called");return HDF_SUCCESS; }// I/O 请求处理函数 int32_t SampleDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) {HDF_LOGI("SampleDriverDispatch called with cmdCode: %d", cmdCode);// 根据 cmdCode 处理不同的命令switch (cmdCode) {case 1:HDF_LOGI("Handling command 1");// 处理命令 1 的逻辑const char *msg = "Hello from driver";struct HdfSBuf *eventData = HdfSbufObtainDefaultSize(); // 创建事件数据缓冲区if (eventData == NULL) {HDF_LOGE("fail to obtain sbuf for event data");return HDF_DEV_ERR_NO_MEMORY;}if (!HdfSbufWriteString(eventData, msg)) { // 写入事件数据HDF_LOGE("fail to write event data");HdfSbufRecycle(eventData);return HDF_FAILURE;}break;case 2:HDF_LOGI("Handling command 2");// 处理命令 2 的逻辑break;default:HDF_LOGE("Unknown command code: %d", cmdCode);return HDF_ERR_INVALID_PARAM;}// 上报事件int ret = HdfDeviceSendEvent(client->device, cmdCode, eventData);HdfSbufRecycle(eventData); // 释放事件数据缓冲区return HDF_SUCCESS; }// 驱动绑定函数 int32_t SampleDriverBind(struct HdfDeviceObject *device) {HDF_LOGI("SampleDriverBind called");if (device == NULL) {HDF_LOGE("Invalid device object");return HDF_FAILURE;}// 定义服务接口static struct ISampleDriverService sampleDriverA = {.ioService.Dispatch = SampleDriverDispatch,.ServiceA = SampleDriverServiceA,.ServiceB = SampleDriverServiceB,};// 将服务接口绑定到设备对象device->service = (struct IDeviceIoService *)&sampleDriverA;return HDF_SUCCESS; }// 驱动卸载函数 void SampleDriverUnload(struct HdfDeviceObject *device) {HDF_LOGI("SampleDriverUnload called");// 在这里释放资源或执行清理操作 }// 驱动描述符 struct HdfDriverEntry g_sampleDriver = {.moduleVersion = 1,.Bind = SampleDriverBind,.Unload = SampleDriverUnload,.moduleName = "sample_driver", };// 驱动模块入口 SYSEXPORT_DRIVER(g_sampleDriver); -
驱动定义消息处理函数中的cmd类型。
#define SAMPLE_WRITE_READ 1 // 读写操作码1 -
用户态获取服务接口并发送消息到驱动。
int SendMsg(const char *testMsg) {if (testMsg == NULL) {HDF_LOGE("test msg is null");return HDF_FAILURE;}struct HdfIoService *serv = HdfIoServiceBind("sample_driver");// 绑定到驱动服务if (serv == NULL) {HDF_LOGE("fail to get service");return HDF_FAILURE;}struct HdfSBuf *data = HdfSbufObtainDefaultSize();// 分配数据和响应缓冲区if (data == NULL) {HDF_LOGE("fail to obtain sbuf data");return HDF_FAILURE;}struct HdfSBuf *reply = HdfSbufObtainDefaultSize();if (reply == NULL) {HDF_LOGE("fail to obtain sbuf reply");ret = HDF_DEV_ERR_NO_MEMORY;goto out;}if (!HdfSbufWriteString(data, testMsg)) {// 写入数据到数据缓冲区HDF_LOGE("fail to write sbuf");ret = HDF_FAILURE;goto out;}int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);// 调用服务的 Dispatch 函数发送请求if (ret != HDF_SUCCESS) {HDF_LOGE("fail to send service call");goto out;} out:HdfSbufRecycle(data);HdfSbbufRecycle(reply);HdfIoServiceRecycle(serv);return ret; } -
用户态接收该驱动上报的消息。
//用户态编写驱动上报消息的处理函数。 static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) {OsalTimespec time;OsalGetTime(&time);HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec);const char *string = HdfSbufReadString(data);if (string == NULL) {HDF_LOGE("fail to read string in event data");return HDF_FAILURE;}HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s", (char *)priv, id, string);return HDF_SUCCESS; } //用户态注册接收驱动上报消息的操作方法。 int RegisterListen() {struct HdfIoService *serv = HdfIoServiceBind("sample_driver");if (serv == NULL) {HDF_LOGE("fail to get service");return HDF_FAILURE;}static struct HdfDevEventlistener listener = {.callBack = OnDevEventReceived,.priv ="Service0"};if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {HDF_LOGE("fail to register event listener");return HDF_FAILURE;}......HdfDeviceUnregisterEventListener(serv, &listener);HdfIoServiceRecycle(serv);return HDF_SUCCESS; }
相关文章:
openharmony中hdf框架的驱动消息机制的实现原理
openharmony中hdf框架的驱动消息机制的实现原理 在分析hdf框架时发现绕来绕去的,整体梳理画了一遍流程图,发现还是有点模糊甚至不清楚如何使用的,详细的每个点都去剖析细节又过于消耗时间,所以有时间便从功能应用的角度一块块的去…...
HTTP SSE 实现
参考: SSE协议 SSE技术详解:使用 HTTP 做服务端数据推送应用的技术 一句概扩 SSE可理解为:服务端和客户端建立连接之后双方均保持连接,但仅支持服务端向客户端推送数据。推送完毕之后关闭连接,无状态行。 下面是基于…...
二分图检测算法以及最大匹配算法(C++)
上一节我们学习了有向图中的最大连通分量. 本节我们来学习二分图. 二分图是一种特殊的图结构, 能够帮助我们高效地解决这些匹配和分配问题. 本文将带你了解二分图的基本概念, 判定方法, 最大匹配算法以及实际应用场景. 环境要求 本文所用样例在Windows 11以及Ubuntu 24.04上面…...
Keepalive基础
一。简介和功能 vrrp协议的软件实现,原生设计目的是为了高可用ipvs服务 功能: 1.基于vrrp协议完成地址流动 2.为vip地址所在的节点生成ipvs规则(在配置文件中预先定义) 3.为ipvs集群的各RS做健康状况检测 4.基于脚本调用接口…...
计算机毕业设计SpringBoot+Vue.jst0图书馆管理系统(源码+LW文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
【Java消息队列】应对消息丢失、重复、顺序与积压的全面策略
应对消息丢失、重复、顺序与积压的全面策略 引言kafka消息丢失生产者消费者重复消费顺序消费消息积压生产者消费者其他RabbitMQ消息丢失生产者事务机制,保证生产者发送消息到 RabbitMQ Server发送方确认机制,保证消息能从交换机路由到指定队列保证消息在 RabbitMQ Server 中的…...
AI大模型学习(三): LangChain(二)
Langchain构建聊天机器人 安装依赖 pip install langchain_community Chat History:它允许聊天机器人"记住"过去的互动,并在回应后续问题时考虑他们 代码 # 创建模型 from langchain_core.messages import HumanMessage from langchain_core.prompts import ChatP…...
apply的用法
apply 是一个在编程语言中常见的函数,它在不同的上下文和语言中有不同的用途。以下是 apply 在常见编程语言中的几种常见用法: 1. Python 中的 apply 方法 在 Python 中,apply 主要用于 pandas 库中的 DataFrame 或 Series 对象,…...
【论文解读】TransMLA: Multi-Head Latent Attention Is All You Need
论文链接 1. 论文背景与问题动机 现代大规模语言模型(LLM)在推理时往往遇到通信瓶颈,主要原因在于自注意力机制中需要缓存大量的 Key-Value(KV)对。例如,对于 LLaMA‑65B 这种模型,即使采用 8…...
CentOS 下安装和配置 HTTPD 服务的详细指南
CentOS 下安装和配置 HTTPD 服务的详细指南 CentOS 下安装和配置 HTTPD 服务的详细指南1. 环境准备2. 安装 HTTPD 服务2.1 更新系统2.2 安装 HTTPD2.3 启动 HTTPD 服务2.4 检查 HTTPD 服务状态 3. 配置防火墙3.1 开放 HTTP 和 HTTPS 端口3.2 验证防火墙规则 4. 配置 HTTPD4.1 主…...
VUE3中子组件改变父组件传过来的值(props)的方法和使用场景详解
在 Vue 3 中,子组件改变父组件传过来的值(props)的方法主要有以下几种:通过事件发射、使用 v-model、模拟 .sync 修饰符的功能(Vue 3 中已移除),以及使用 ref 或 reactive。下面我将结合代码示例…...
登录-06.JWT令牌-生成和校验
一.JWT令牌的生成和校验 JWT令牌生成 想要生成JWT令牌,那么就要首先引入JWT令牌的相关依赖, <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version>…...
【Git】多人协作
文章目录 完成准备工作多人协作场景一场景二远程分支删除后,本地 git branch -a 依然能看到的解决办法 完成准备工作 在之前,我们所完成的工作如下: 基本完成 Git 的所有本地库的相关操作,git基本操作,分支理解&#…...
Python爬虫-破解字体加密技术
前言 本文是该专栏的第77篇,后面会持续分享python爬虫干货知识,记得关注。 字体加密是一种常见的反爬虫技术,通过自定义字体文件和字符映射来保护网页内容,防止爬虫直接获取文本信息。 在文章《Python爬虫-猫眼电影的影院数据》中,笔者有详细介绍过猫眼的相关数据采集。…...
邮件安全之发件人伪造
电子邮件工作原理 电子邮件传输过程中主要涉及到SMTP、IMAP、POP3三种协议,具体功能如下: SMTP:全称Simple Mail Transfer Protocol,即简单邮件传输协议,主要用于发送邮件,使用端口号25。 IMAP:全称Internet Mail Acce…...
git 常用功能
以下是 Git 的常用功能及其命令: 初始化仓库 git init在当前目录初始化一个新的 Git 仓库。 克隆仓库 git clone <仓库地址>将远程仓库克隆到本地。 查看状态 git status查看工作区和暂存区的状态。 添加文件到暂存区 git add <文件名>将文件添…...
【llm落地】从零到一,用DeepSeek打造智能BI工具:自然语言驱动数据洞察
在数据驱动的时代,商业智能 (BI) 工具已经成为企业决策的关键。然而,传统的 BI 工具往往操作复杂,需要专业技能才能驾驭。想象一下,如果用户只需要用 自然语言 就能轻松查询数据、获取分析结果甚至生成可视化图表,那将会多么高效和便捷! 本文将带你踏上从零到一构建智能…...
请谈谈 Vue 中的 key 属性的重要性,如何确保列表项的唯一标识?
1. Key属性的核心作用(附代码对比) // 错误示例:未使用key的列表渲染 <template><ul><li v-for"item in items">{{ item.text }}</li></ul> </template>// 正确示例:使用唯一key的…...
使用 AIStor 和 OpenSearch 增强搜索功能
在这篇文章中,我们将探讨搜索,特别是 OpenSearch 如何帮助我们识别模式或查看不断增长的数据中的趋势。例如,如果您正在查看运营数据,如果您的服务似乎是随机的,那么您需要尽可能回溯以识别模式并找出原因。这不仅适用…...
Node.js中如何修改全局变量的几种方式
Node.js中如何修改全局变量。我需要先理解他们的需求。可能他们是在开发过程中遇到了需要跨模块共享数据的情况,或者想要配置一些全局可访问的设置。不过,使用全局变量可能存在一些问题,比如命名冲突、难以维护和测试困难,所以我得…...
基于Python和Neo4j开发的医疗辅助诊断系统的详细实现步骤和代码示例
以下是一个基于Python和Neo4j开发的医疗辅助诊断系统的详细实现步骤和代码示例。 1. 环境准备 首先,确保你已经安装了必要的库。可以使用以下命令进行安装: pip install py2neo2. Neo4j数据库初始化 在Neo4j中创建一个新的数据库,并启动N…...
第9章:LangChain结构化输出-示例2(数字提取服务)
如何使用LangChain4j框架创建和使用多种AI服务。它通过定义接口和注解,将自然语言处理任务(如情感分析、数字提取、日期提取、POJO提取等)封装为服务,并通过LangChain4j的AiServices动态生成这些服务的实现。 本章主要讲述基于Lan…...
【LLM】R1复现项目(SimpleRL、OpenR1、LogitRL、TinyZero)持续更新
note (1)未来的工作需亟待解决: 支持大规模 RL 训练(PPO、GRPO 等)的开源基础框架用于稳定训练的 GRPO 训练超参的自动化调优RL 训练数据的配比(难度、领域、任务等)基于 Instruct 模型训练 R…...
买股票的最佳时机 - 2
买卖股票的最佳时机 III 题目描述: 提示: 1 < prices.length < 1050 < prices[i] < 105 分析过程: 写动态规划,我们需要考虑一下问题: 定义状态状态转移方程初始条件 遍历顺序 4种状态: …...
Python基于flask的智慧交通可视化,大数据智慧交通数据可视化系统
博主介绍:✌程序员徐师兄、8年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战*✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
【Unity】鱼群效果模拟
鱼群效果模拟 文章目录 鱼群效果模拟Boid算法实现方式version1_CPUversion2_GPUversion3_Multilaterationversion4_Bitonic_Sorting (GPU友好)version5_Skinning (TODO) 细节项优化项参考链接 Boid算法 Boid算法是一种模拟群体行…...
Unity使用IL2CPP打包时,我们应该注意什么?如何避免(可以举例说明)
这一篇部分内容在Unity之中如何处理C#底层代码那篇博客之中有提及,接下来对这部分进行补充说明。(请先阅读--Unity之中如何处理C#底层代码) 目录 1 注意点 2 如何避免 1 注意点 IL2CPP 在编译过程中会将 IL(中间语言…...
Wireshark使用介绍
文章目录 Wireshark介绍Wireshark使用工作模式介绍1. 混杂模式(Promiscuous Mode)2. 普通模式(Normal Mode)3. 监视模式(Monitor Mode) 界面分区捕获过滤器语法基本语法逻辑运算符高级语法使用示例捕获过滤…...
云图库平台(五)——后端图片模块开发
目录 一、需求分析二、库表设计三、图片的处理如何实现图片的上传和下载创建图片的业务流程如何对图片进行解析 四、创建并使用对象存储五、后端操作对象存储初始化客户端通用能力类文档上传文件下载 一、需求分析 管理员功能: 图片的上传和创建:仅管理…...
postman调用ollama的api
按照如下设置,不需要设置key 保持长会话的方法 # 首次请求 curl http://localhost:11434/api/generate -d {"model": "deepseek-r1:32b","prompt": "请永久记住:110,1-12,之后所有数学计算必…...
