OpenHarmony - 驱动使用指南,HDF驱动开发流程
OpenHarmony - HDF驱动开发流程
概述
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路,让驱动开发和部署更加规范,旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的驱动管理的开发环境,力求做到一次开发,多系统部署。
驱动加载
HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能。
驱动服务管理
HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。
驱动消息机制
HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。
配置管理
HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。
驱动模型
HDF框架将一类设备驱动放在同一个Host(设备容器)里面,用于管理一组设备的启动加载等过程。在划分Host时,驱动程序是部署在一个Host还是部署在不同的Host,主要考虑驱动程序之间是否存在耦合性,如果两个驱动程序之间存在依赖,可以考虑将这部分驱动程序部署在一个Host里面,否则部署到独立的Host中是更好的选择。Device对应一个真实的物理设备。DeviceNode是设备的一个部件,Device至少有一个DeviceNode。每个DeviceNode可以发布一个设备服务。驱动即驱动程序,每个DevicdNode唯一对应一个驱动,实现和硬件的功能交互。HDF驱动模型如下图所示:
图1 HDF驱动模型
功能描述
驱动加载
HDF驱动框架提供把和配置的设备列表匹配成功的驱动程序加载起来的功能,支持按需加载和按序加载两种策略,具体设备的加载策略由配置文件中的preload字段来控制,配置值参考如下:
typedef enum {DEVICE_PRELOAD_ENABLE = 0,DEVICE_PRELOAD_ENABLE_STEP2 = 1,DEVICE_PRELOAD_DISABLE = 2,DEVICE_PRELOAD_INVALID
} DevicePreload;
按需加载
- preload字段配置为0(DEVICE_PRELOAD_ENABLE),则系统启动过程中默认加载。
- preload字段配置为1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快速启动的时候,则在系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE含义相同。
- preload字段配置为2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务消息机制时,如果驱动服务不存在,HDF框架会尝试动态加载该驱动。
按序加载(默认加载策略)
配置文件中的priority(取值范围为整数0到200)是用来表示host(驱动容器)和驱动的优先级的。不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。
异常恢复(用户态驱动)
当驱动服务异常退出时,恢复策略如下:
- preload字段配置为0(DEVICE_PRELOAD_ENABLE)或1(DEVICE_PRELOAD_ENABLE_STEP2)的驱动服务,由启动模块拉起host并重新加载服务。
- preload字段配置为2(DEVICE_PRELOAD_DISABLE)的驱动服务,需业务模块注册HDF的服务状态监听器,当收到服务退出消息时,业务模块调用LoadDevice重新加载服务。
驱动服务管理
驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取。HDF框架定义了驱动对外发布服务的策略,由配置文件中的policy字段来控制,policy字段的取值范围以及含义如下:
typedef enum {/* 驱动不提供服务 */SERVICE_POLICY_NONE = 0,/* 驱动对内核态发布服务 */SERVICE_POLICY_PUBLIC = 1,/* 驱动对内核态和用户态都发布服务 */SERVICE_POLICY_CAPACITY = 2,/* 驱动服务不对外发布服务,但可以被订阅 */SERVICE_POLICY_FRIENDLY = 3,/* 驱动私有服务不对外发布服务,也不能被订阅 */SERVICE_POLICY_PRIVATE = 4,/* 错误的服务策略 */SERVICE_POLICY_INVALID
} ServicePolicy;
使用场景
当驱动需要以接口的形式对外提供能力时,可以使用HDF框架的驱动服务管理能力。
接口说明
针对驱动服务管理功能,HDF框架开放了以下接口供开发者调用,如下表所示:
表1 服务管理接口
方法 | 描述 |
---|---|
int32_t (*Bind)(struct HdfDeviceObject *deviceObject) | 需要驱动开发者实现Bind函数,将自己的服务接口绑定到HDF框架中。 |
const struct HdfObject *DevSvcManagerClntGetService(const char *svcName) | 获取驱动的服务。 |
int HdfDeviceSubscribeService( struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback) | 订阅驱动的服务。 |
驱动消息机制管理
使用场景
当用户态应用和内核态驱动需要交互时,可以使用HDF框架的消息机制来实现。
接口说明
消息机制的功能主要有以下两种:
- 用户态应用发送消息到驱动。
- 用户态应用接收驱动主动上报事件。
表2 消息机制接口
方法 | 描述 |
---|---|
struct HdfIoService *HdfIoServiceBind(const char *serviceName); | 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。 |
void HdfIoServiceRecycle(struct HdfIoService *service); | 释放驱动服务。 |
int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | 用户态程序注册接收驱动上报事件的操作方法。 |
int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data) | 驱动主动上报事件接口。 |
配置管理
配置概述
HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。HC-GEN(HDF Configuration Generator)是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:
- 在弱性能环境中,转换为配置树源码或配置树宏定义,驱动可直接调用C代码或宏式APIs获取配置。
- 在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。
以下是使用HCB模式的典型应用场景:
图2 配置使用流程图
HCS经过HC-GEN编译生成HCB文件,HDF驱动框架中的HCS Parser模块会从HCB文件中重建配置树,HDF驱动模块使用HCS Parser提供的配置读取接口获取配置内容。
配置语法
HCS的语法介绍如下:
关键字
HCS配置语法保留了以下关键字。
表3 HCS配置语法保留关键字
关键字 | 用途 | 说明 |
---|---|---|
root | 配置根节点 | - |
include | 引用其他HCS配置文件 | - |
delete | 删除节点或属性 | 只能用于操作include导入的配置树 |
template | 定义模板节点 | - |
match_attr | 用于标记节点的匹配查找属性 | 解析配置时可以使用该属性的值查找到对应节点 |
基本结构
HCS主要分为属性(Attribute)和节点(Node)两种结构。
属性
属性即最小的配置单元,是一个独立的配置项。语法如下:
attribute_name = value;
- attribute_name是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。
- value的可用格式如下:
- 数字常量,支持二进制、八进制、十进制、十六进制数,具体数据类型章节。
- 字符串,内容使用双引号("")引用。
- 节点引用。
- attribute必须以分号(;)结束且必须属于一个node。
节点
节点是一组属性的集合,语法如下:
node_name {module = "sample";...}
- node_name是字母、数字、下划线的组合且必须以字母或下划线开头,字母区分大小写。
- 大括号后无需添加结束符“;”。
- root为保留关键字,用于声明配置表的根节点。每个配置表必须以root节点开始。
- root节点中必须包含module属性,其值应该为一个字符串,用于表征该配置所属模块。
- 节点中可以增加match_attr属性,其值为一个全局唯一的字符串。当驱动程序在解析配置时可以以该属性的值为参数调用查找接口查找到包含该属性的节点。
数据类型
在属性定义中使用自动数据类型,不显式指定类型,属性支持的数据类型如下:
整型
整型长度自动推断,根据实际数据长度给与最小空间占用的类型。
- 二进制,0b前缀,示例:0b1010。
- 八进制,0前缀,示例:0664。
- 十进制 ,无前缀,且支持有符号与无符号,示例:1024,+1024均合法。驱动程序在读取负值时注意使用有符号数读取接口。
- 十六进制,0x前缀,示例:0xff00、0xFF。
字符串
字符串使用双引号("")表示。
数组
数组元素支持整型、字符串,不支持混合类型。整型数组中uint32_t uint64_t混用会向上转型为uint64_t数组。整型数组与字符串数组示例如下:
attr_foo = [0x01, 0x02, 0x03, 0x04];
attr_bar = ["hello", "world"];
bool类型
bool类型中true表示真,false表示假。
预处理
include
用于导入其他HCS文件。语法示例如下:
#include "foo.hcs"
#include "../bar.hcs"
- 文件名必须使用双引号(""),不在同一目录使用相对路径引用。被include文件也必须是合法的HCS文件。
- 多个include,如果存在相同的节点,后者覆盖前者,其余的节点依次展开。
注释
支持两种注释风格。
-
单行注释。
// comment
-
多行注释。
/* comment */
说明: 多行注释不支持嵌套。
引用修改
引用修改的作用是在当前节点中修改另外任意一个节点的内容,语法为:
node :& source_node
上述语句表示node中的内容是对source_node节点内容的修改。示例如下:
root {module = "sample";foo {foo_ :& root.bar{attr = "foo";}foo1 :& foo2 {attr = 0x2;}foo2 {attr = 0x1;}}bar {attr = "bar";}
}
最终生成配置树为:
root {module = "sample";foo {foo2 {attr = 0x2;}}bar {attr = "foo";}
}
在以上示例中,可以看到foo.foo_节点通过引用将bar.attr属性的值修改为了"foo",foo.foo1节点通过引用将foo.foo2.attr属性的值修改为了0x2。foo.foo_以及foo.foo1节点表示对目标节点内容的修改,其自身并不会存在最终生成的配置树中。
- 引用同级node,可以直接使用node名称,否则被引用的节点必须使用绝对路径,节点间使用“.”分隔,root表示根节点,格式为root开始的节点路径序列,例如root.foo.bar即为一个合法的绝对路径。
- 如果出现修改冲突(即多处修改同一个属性),编译器将提示warning,因为这种情况下只会生效某一个修改而导致最终结果不确定。
节点复制
节点复制可以实现在节点定义时从另一个节点先复制内容,用于定义内容相似的节点。语法为:
node : source_node
上述语句表示在定义"node"节点时将另一个节点"source_node"的属性复制过来。示例如下:
root {module = "sample";foo {attr_0 = 0x0;}bar:foo {attr_1 = 0x1;}
}
上述代码的最终生成配置树为:
root {module = "sample";foo {attr_0 = 0x0;}bar {attr_1 = 0x1;attr_0 = 0x0;}
}
在上述示例中,编译后bar节点既包含attr_0属性又包含attr_1属性,在bar中对attr_0的修改不会影响到foo。
当foo和bar在同级node中时可不指定foo的路径,否则需要使用绝对路径引用,绝对路径的介绍请参考引用修改。
删除
要对include导入的base配置树中不需要的节点或属性进行删除,可以使用delete关键字。下面的举例中sample1.hcs通过include导入了sample2.hcs中的配置内容,并使用delete删除了sample2.hcs中的attribute2属性和foo_2节点,示例如下:
// sample2.hcs
root {attr_1 = 0x1;attr_2 = 0x2;foo_2 {t = 0x1;}
}// sample1.hcs
#include "sample2.hcs"
root {attr_2 = delete;foo_2 : delete {}
}
上述代码在生成过程中将会删除root.foo_2节点与attr_2,最终生成配置树为:
root {attr_1 = 0x1;
}
说明: 在同一个HCS文件中不允许使用delete,建议直接删除不需要的属性。
属性引用
为了在解析配置时快速定位到关联的节点,可以把节点作为属性的右值,通过读取属性查找到对应节点。语法为:
attribute = &node;
上述语句表示attribute的值是一个节点node的引用,在解析时可以用这个attribute快速定位到node,便于关联和查询其他node。示例如下:
node1 {attributes;
}
node2 {attr_1 = &root.node1;
}
或
node2 {node1 {attributes;}attr_1 = &node1;
}
模板
模板的用途在于生成严格一致的node结构,以便对同类型node进行遍历和管理。使用template关键字定义模板node,子node通过双冒号“::”声明继承关系。子节点可以改写或新增但不能删除template中的属性,子节点中没有定义的属性将使用template中的定义作为默认值。示例如下:
root {module = "sample";template foo {attr_1 = 0x1;attr_2 = 0x2;}bar :: foo {}bar_1 :: foo {attr_1 = 0x2;}
}
生成配置树如下:
root {module = "sample";bar {attr_1 = 0x1;attr_2 = 0x2;}bar_1 {attr_1 = 0x2;attr_2 = 0x2;}
}
在上述示例中,bar和bar_1节点继承了foo节点,生成配置树节点结构与foo保持了完全一致,只是属性的值不同。
配置生成
hc-gen是配置生成的工具,可以对HCS配置语法进行检查并把HCS源文件转化成HCB二进制文件。
hc-gen介绍
hc-gen参数说明:
Usage: hc-gen [Options] [File]
options:-o <file> output file name, default same as input-a hcb align with four bytes-b output binary output, default enable-t output config in C language source file style-m output config in macro source file style-i output binary hex dump in C language source file style-p <prefix> prefix of generated symbol name-d decompile hcb to hcs-V show verbose info-v show version-h show this help message
生成.c/.h配置文件方法:
hc-gen -o [OutputCFileName] -t [SourceHcsFileName]
生成HCB配置文件方法:
hc-gen -o [OutputHcbFileName] -b [SourceHcsFileName]
生成宏定义配置文件方法:
hc-gen -o [OutputMacroFileName] -m [SourceHcsFileName]
反编译HCB文件为HCS方法:
hc-gen -o [OutputHcsFileName] -d [SourceHcbFileName]
开发指导
场景介绍
关于驱动的开发我们主要目的是实现驱动代码的编写,但是驱动开发过程中需要服务管理、消息机制管理,才能使驱动在代码编译过程中进行加载。以下开发步骤中介绍了驱动开发、驱动消息机制管理开发、驱动服务管理开发的步骤。
驱动开发实例
基于HDF框架的驱动开发主要分为三个部分:驱动实现、驱动编译脚本编写和驱动配置。详细开发流程如下所示:
驱动实现
驱动实现包含驱动业务代码实现和驱动入口注册,具体写法如下:
-
驱动业务代码
#include "hdf_device_desc.h" // HDF框架对驱动开发相关能力接口的头文件 #include "hdf_log.h" // HDF框架提供的日志接口头文件#define HDF_LOG_TAG sample_driver // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签。// 将驱动对外提供的服务能力接口绑定到HDF框架。 int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) {HDF_LOGD("Sample driver bind success");return HDF_SUCCESS; }// 驱动自身业务初始化的接口 int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) {HDF_LOGD("Sample driver Init success");return HDF_SUCCESS; }// 驱动资源释放的接口 void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) {HDF_LOGD("Sample driver release success");return; }
-
驱动入口注册到HDF框架
// 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量。 struct HdfDriverEntry g_sampleDriverEntry = {.moduleVersion = 1,.moduleName = "sample_driver",.Bind = HdfSampleDriverBind,.Init = HdfSampleDriverInit,.Release = HdfSampleDriverRelease, };// 调用HDF_INIT将驱动入口注册到HDF框架中。在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动;当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 HDF_INIT(g_sampleDriverEntry);
驱动编译脚本编写
-
LiteOS
涉及Makefile和BUILD.gn修改:
-
Makefile部分:
驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。
include $(LITEOSTOPDIR)/../../drivers/hdf_core/adapter/khdf/liteos/lite.mk # 【必需】导入hdf预定义内容 MODULE_NAME := #生成的结果文件 LOCAL_INCLUDE := #本驱动的头文件目录 LOCAL_SRCS := #本驱动的源代码文件 LOCAL_CFLAGS := #自定义的编译选项 include $(HDF_DRIVER) #导入Makefile模板完成编译
编译结果文件链接到内核镜像,添加到drivers/hdf_core/adapter/khdf/liteos目录下的hdf_lite.mk里面,示例如下:
LITEOS_BASELIB += -lxxx #链接生成的静态库 LIB_SUBDIRS += #驱动代码Makefile的目录
-
BUILD.gn部分:
添加模块BUILD.gn,可参考如下示例:
import("//build/lite/config/component/lite_component.gni") import("//drivers/hdf_core/adapter/khdf/liteos/hdf.gni") module_switch = defined(LOSCFG_DRIVERS_HDF_xxx) module_name = "xxx" hdf_driver(module_name) {sources = ["xxx/xxx/xxx.c", #模块要编译的源码文件]public_configs = [ ":public" ] #使用依赖的头文件配置 } config("public") { #定义依赖的头文件配置include_dirs = ["xxx/xxx/xxx", #依赖的头文件目录] }
把新增模块的BUILD.gn所在的目录添加到**/drivers/hdf_core/adapter/khdf/liteos/BUILD.gn**里面:
group("liteos") {public_deps = [ ":$module_name" ]deps = ["xxx/xxx",#新增模块BUILD.gn所在的目录,/drivers/hdf_core/adapter/khdf/liteos] }
-
-
Linux
如果需要定义模块控制宏,需要在模块目录xxx里面添加Kconfig文件,并把Kconfig文件路径添加到drivers/hdf_core/adapter/khdf/linux/Kconfig里面:
source "drivers/hdf/khdf/xxx/Kconfig" #目录为hdf模块软链接到kernel里面的目录
添加模块目录到drivers/hdf_core/adapter/khdf/linux/Makefile:
obj-$(CONFIG_DRIVERS_HDF) += xxx/
在模块目录xxx里面添加Makefile文件,在Makefile文件里面添加模块代码编译规则:
obj-y += xxx.o
驱动配置
HDF使用HCS作为配置描述源码,HCS详细介绍配置管理。
驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下:
-
驱动设备描述(必选)
HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述。驱动的设备描述填写如下所示:
root {device_info {match_attr = "hdf_manager";template host { // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省。hostName = "";priority = 100;uid = ""; // 用户态进程uid,缺省为空,会被配置为hostName的定义值,即普通用户。gid = ""; // 用户态进程gid,缺省为空,会被配置为hostName的定义值,即普通用户组。caps = [""]; // 用户态进程Linux capabilities配置,缺省为空,需要业务模块按照业务需要进行配置。template device {template deviceNode {policy = 0;priority = 100;preload = 0;permission = 0664;moduleName = "";serviceName = "";deviceMatchAttr = "";}}}sample_host :: host{hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器。priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序。caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"]; // 用户态进程Linux capabilities配置。device_sample :: device { // sample设备节点device0 :: deviceNode { // sample驱动的DeviceNode节点policy = 1; // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍。priority = 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序。preload = 0; // 驱动按需加载字段。permission = 0664; // 驱动创建设备节点权限moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一。deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等。}}}} }
说明:
- uid、gid、caps等配置项是用户态驱动的启动配置,内核态不用配置。
- 根据进程权限最小化设计原则,业务模块uid、gid不用配置,如上面的sample_host,使用普通用户权限,即uid和gid被定义为hostName的定义值。
- 如果普通用户权限不能满足业务要求,需要把uid、gid定义为system或者root权限时,请找安全专家进行评审。
- 进程的uid在文件base/startup/init/services/etc/passwd中配置,进程的gid在文件base/startup/init/services/etc/group中配置,进程uid和gid配置参考:系统服务用户组添加方法。
- caps值:格式为caps = ["xxx"],如果要配置CAP_DAC_OVERRIDE,此处需要填写caps = ["DAC_OVERRIDE"],不能填写为caps = ["CAP_DAC_OVERRIDE"]。
- preload:驱动按需加载字段。
-
驱动私有配置信息(可选)
如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息。HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject中的property里面,通过Bind和Init(参考驱动实现)传递给驱动。驱动的配置信息示例如下:
root {SampleDriverConfig {sample_version = 1;sample_bus = "I2C_0";match_attr = "sample_config"; // 该字段的值必须和device_info.hcs中的deviceMatchAttr值一致} }
配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs,示例如下:
#include "device_info/device_info.hcs" #include "sample/sample_config.hcs"
驱动消息机制管理开发
-
将驱动配置信息中服务策略policy字段设置为2(SERVICE_POLICY_CAPACITY,policy定义)。
device_sample :: Device {policy = 2;... }
-
配置驱动信息中的服务设备节点权限(permission字段)是框架给驱动创建设备节点的权限,默认是0666,驱动开发者根据驱动的实际使用场景配置驱动设备节点的权限。
-
在服务实现过程中,实现服务基类成员IDeviceIoService中的Dispatch方法。
// Dispatch是用来处理用户态发下来的消息 int32_t SampleDriverDispatch(struct HdfDeviceIoClient *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) {HDF_LOGI("sample driver lite A dispatch");return HDF_SUCCESS; } int32_t SampleDriverBind(struct HdfDeviceObject *device) {HDF_LOGI("test for lite os sample driver A Open!");if (device == NULL) {HDF_LOGE("test for lite os sample driver A Open failed!");return HDF_FAILURE;}static struct ISampleDriverService sampleDriverA = {.ioService.Dispatch = SampleDriverDispatch,.ServiceA = SampleDriverServiceA,.ServiceB = SampleDriverServiceB,};device->service = (struct IDeviceIoService *)(&sampleDriverA);return HDF_SUCCESS; }
-
驱动定义消息处理函数中的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);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; }
-
驱动上报事件。
int32_t SampleDriverDispatch(HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) {... // process api call herereturn HdfDeviceSendEvent(client->device, cmdCode, data); }
-
驱动服务管理开发
驱动服务管理的开发包括驱动服务的编写、绑定、获取或者订阅,详细步骤如下。
驱动服务编写
// 驱动服务结构的定义
struct ISampleDriverService {struct IDeviceIoService ioService; // 服务结构的首个成员必须是IDeviceIoService类型的成员。int32_t (*ServiceA)(void); // 驱动的第一个服务接口。int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加。
};// 驱动服务接口的实现
int32_t SampleDriverServiceA(void)
{// 驱动开发者实现业务逻辑return HDF_SUCCESS;
}int32_t SampleDriverServiceB(uint32_t inputCode)
{// 驱动开发者实现业务逻辑return HDF_SUCCESS;
}
驱动服务绑定
开发者实现HdfDriverEntry中的Bind指针函数,如下的SampleDriverBind,把驱动服务绑定到HDF框架中。
int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{// deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。if (deviceObject == NULL) {HDF_LOGE("Sample device object is null!");return HDF_FAILURE;}static struct ISampleDriverService sampleDriverA = {.ServiceA = SampleDriverServiceA,.ServiceB = SampleDriverServiceB,};deviceObject->service = &sampleDriverA.ioService;return HDF_SUCCESS;
}
驱动服务获取
应用程序开发者获取驱动服务有两种方式:通过HDF接口直接获取和通过HDF提供的订阅机制获取。
通过HDF接口直接获取
当驱动服务获取者明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:
const struct ISampleDriverService *sampleService =(const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
if (sampleService == NULL) {return HDF_FAILURE;
}
sampleService->ServiceA();
sampleService->ServiceB(5);
通过HDF提供的订阅机制获取
当内核态驱动服务获取者对驱动(同一个host)加载的时机不感知时,可以通过HDF框架提供的订阅机制来订阅该驱动服务。当该驱动加载完成时,HDF框架会将被订阅的驱动服务发布给订阅者(驱动服务获取者),实现方式如下所示:
// 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用。
// object为订阅者的私有数据,service为被订阅的服务对象。
int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
{const struct ISampleDriverService *sampleService =(const struct ISampleDriverService *)service;if (sampleService == NULL) {return HDF_FAILURE;}sampleService->ServiceA();sampleService->ServiceB(5);
}
// 订阅过程的实现
int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
{if (deviceObject == NULL) {HDF_LOGE("Test driver init failed, deviceObject is null!");return HDF_FAILURE;}struct SubscriberCallback callBack;callBack.deviceObject = deviceObject;callBack.OnServiceConnected = TestDriverSubCallBack;int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);if (ret != HDF_SUCCESS) {HDF_LOGE("Test driver subscribe sample driver failed!");}return ret;
}
HDF开发实例
下面基于HDF框架,提供一个完整的样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。
添加配置
在HDF框架的配置文件(例如vendor/hisilicon/xxx/hdf_config/device_info)中添加该驱动的配置信息,如下所示:
root {device_info {match_attr = "hdf_manager";template host {hostName = "";priority = 100;template device {template deviceNode {policy = 0;priority = 100;preload = 0;permission = 0664;moduleName = "";serviceName = "";deviceMatchAttr = "";}}}sample_host :: host {hostName = "sample_host";sample_device :: device {device0 :: deviceNode {policy = 2;priority = 100;preload = 1;permission = 0664;moduleName = "sample_driver";serviceName = "sample_service";}}}}
}
编写驱动代码
基于HDF框架编写的sample驱动代码如下:
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include "hdf_log.h"
#include "hdf_base.h"
#include "hdf_device_desc.h"#define HDF_LOG_TAG sample_driver#define SAMPLE_WRITE_READ 123static int32_t HdfSampleDriverDispatch(struct HdfDeviceIoClient *client, int id, struct HdfSBuf *data, struct HdfSBuf *reply)
{HDF_LOGI("%{public}s: received cmd %{public}d", __func__, id);if (id == SAMPLE_WRITE_READ) {const char *readData = HdfSbufReadString(data);if (readData != NULL) {HDF_LOGE("%{public}s: read data is: %{public}s", __func__, readData);}if (!HdfSbufWriteInt32(reply, INT32_MAX)) {HDF_LOGE("%{public}s: reply int32 fail", __func__);}return HdfDeviceSendEvent(client->device, id, data);}return HDF_FAILURE;
}static void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject)
{// release resources herereturn;
}static int HdfSampleDriverBind(struct HdfDeviceObject *deviceObject)
{if (deviceObject == NULL) {return HDF_FAILURE;}static struct IDeviceIoService testService = {.Dispatch = HdfSampleDriverDispatch,};deviceObject->service = &testService;return HDF_SUCCESS;
}static int HdfSampleDriverInit(struct HdfDeviceObject *deviceObject)
{if (deviceObject == NULL) {HDF_LOGE("%{public}s::ptr is null!", __func__);return HDF_FAILURE;}HDF_LOGI("Sample driver Init success");return HDF_SUCCESS;
}static struct HdfDriverEntry g_sampleDriverEntry = {.moduleVersion = 1,.moduleName = "sample_driver",.Bind = HdfSampleDriverBind,.Init = HdfSampleDriverInit,.Release = HdfSampleDriverRelease,
};HDF_INIT(g_sampleDriverEntry);
编写用户程序和驱动交互代码
基于HDF框架编写的用户态程序和驱动交互的代码如下(代码可以放在目录drivers/hdf_core/adapter/uhdf下面编译,BUILD.gn可以参考drivers/hdf_core/framework/sample/platform/uart/dev/BUILD.gn):
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "hdf_log.h"
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"#define HDF_LOG_TAG sample_test
#define SAMPLE_SERVICE_NAME "sample_service"#define SAMPLE_WRITE_READ 123int g_replyFlag = 0;static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data)
{const char *string = HdfSbufReadString(data);if (string == NULL) {HDF_LOGE("fail to read string in event data");g_replyFlag = 1;return HDF_FAILURE;}HDF_LOGI("%{public}s: dev event received: %{public}u %{public}s", (char *)priv, id, string);g_replyFlag = 1;return HDF_SUCCESS;
}static int SendEvent(struct HdfIoService *serv, char *eventData)
{int ret = 0;struct HdfSBuf *data = HdfSbufObtainDefaultSize();if (data == NULL) {HDF_LOGE("fail to obtain sbuf data");return 1;}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, eventData)) {HDF_LOGE("fail to write sbuf");ret = HDF_FAILURE;goto out;}ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);if (ret != HDF_SUCCESS) {HDF_LOGE("fail to send service call");goto out;}int replyData = 0;if (!HdfSbufReadInt32(reply, &replyData)) {HDF_LOGE("fail to get service call reply");ret = HDF_ERR_INVALID_OBJECT;goto out;}HDF_LOGI("Get reply is: %{public}d", replyData);
out:HdfSbufRecycle(data);HdfSbufRecycle(reply);return ret;
}int main()
{char *sendData = "default event info";struct HdfIoService *serv = HdfIoServiceBind(SAMPLE_SERVICE_NAME);if (serv == NULL) {HDF_LOGE("fail to get service %s", SAMPLE_SERVICE_NAME);return HDF_FAILURE;}static struct HdfDevEventlistener listener = {.callBack = OnDevEventReceived,.priv ="Service0"};if (HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCCESS) {HDF_LOGE("fail to register event listener");return HDF_FAILURE;}if (SendEvent(serv, sendData)) {HDF_LOGE("fail to send event");return HDF_FAILURE;}while (g_replyFlag == 0) {sleep(1);}if (HdfDeviceUnregisterEventListener(serv, &listener)) {HDF_LOGE("fail to unregister listener");return HDF_FAILURE;}HdfIoServiceRecycle(serv);return HDF_SUCCESS;
}
说明: 用户态应用程序使用了HDF框架中的消息发送接口,因此在编译用户态程序的过程中需要依赖HDF框架对外提供的hdf_core和osal的动态库,在gn编译文件中添加如下依赖项:
deps = [
"//drivers/hdf_core/adapter/uhdf/manager:hdf_core",
"//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal",
]
相关文章:
OpenHarmony - 驱动使用指南,HDF驱动开发流程
OpenHarmony - HDF驱动开发流程 概述 HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路,让驱动开发…...
C++负载均衡远程调用学习之UDP SERVER功能
目录 1.LARSV0.9-配置功能 2.LARSV0.10-upd-server的实现 3.LARSV0.10-udp-client的实现 1.LARSV0.9-配置功能 2.LARSV0.10-upd-server的实现 3.LARSV0.10-udp-client的实现...
word交叉引用图片、表格——只引用编号的处理方法
交叉引用图片/表格 在“引用”选项卡上的“题注”组中,单击“插入题注”。勾选【从题注中排除标签】。在文中插入题注。 【注 意】 这时候插入的题注只有编号项了。然后手动打上标签【TABLE】,并在标签和编号项之间加上【样式分隔符,AltCt…...
平台介绍-开放API接口-鉴权
平台的理念是一个组织内部只建一套系统。但是现实情况是,组织内部已经建立了很多系统,是不能一次性替代的,只能先搭起平台,然后逐步开始替换。这样就不可避免的存在其他系统和平台进行交互的问题。 平台为此设计了开放API接口。其…...
【Bootstrap V4系列】 学习入门教程之 组件-警告框(Alert)
Bootstrap V4 学习入门教程之 组件-警告框(Alert) 警告框(Alert)一、示例二、链接的颜色三、添加其它内容四、关闭警告框 通过 JavaScript 触发行为触发器本组件所暴露的事件 警告框(Alert) 通过精炼且灵活…...
【服务器通信-socket】——int socket(int domain, int type, int protocol);
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); domain: AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址 AF_INET6 与上面类似,不过是来用IPv6的地…...
洛谷P1014(Cantor 表[NOIP 1999 普及组])题解
题目大意:求Cantor表(按照Z字形排列(如第一项是1/1,然后是1/2,2/1,3/1,2/2))的第N项。 那么,我们需要找出Cantor表的排列规律。根据题目中的Z字形描述&#x…...
【愚公系列】《Manus极简入门》012-自我认知顾问:“内在探索向导”
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! …...
密码学_加密
目录 密码学 01 密码基础进制与计量 02 加解密基操 替换 移位 编码 编码 置换 移位 加解密强度 03 对称加密算法(私钥) 工作过程 缺陷 对称加密算法列举? DES DES算法架构 DES分组加密公式 DES中ECB-CBC两种加密方式 3DES 由于DES密钥太短…...
w317汽车维修预约服务系统设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...
云盘系统设计
需求背景 网盘面向大量C端用户 1000w用户 DAU 20% 每天10次 QPS: 1000w * 0.2 * 10 / 100k 500 峰值估计:500 * 5 2500 功能需求 支持上传,下载,多端共同在线编辑,数据冲突处理 非功能需求 1.latency 20s左右 2.可用性与…...
西电雨课堂《知识产权法》课后作业答案
目录 第 1 章 1.1 课后作业 1.2 课后作业 第 2 章 2.1 课后作业 2.2 课后作业 2.3 课后作业 2.4 课后作业 2.5 课后作业 2.6 课后作业 2.7 课后作业 2.8 课后作业 2.9 课后作业 2.10 课后作业 第 3 章 3.1 课后作业 3.2 课后作业 3.3 课后作业 3…...
通信协议记录仪-产品规格书
以下是为 通信协议记录仪(ProtoLogger Pro) 的详细产品规格书,覆盖 技术细节、场景需求、竞品差异化,确保可作为产品开发、市场营销及竞品分析的核心依据。 通信协议记录仪产品规格书 产品名称:ProtoLogger Pro(中文名称:蹲守…...
订单系统冷热分离方案:优化性能与降低存储成本
随着时间推移,订单数据不断积累。在电商平台或者服务型应用中,订单数据是核心数据之一。然而,随着数据量的增长,如何高效存储、管理和查询这些数据成为了系统架构设计的重要问题。在大多数情况下,订单数据的处理不仅涉…...
数据结构学习笔记
第 1 章 绪论 【考纲内容】 (一)数据结构的基本概念 (二)算法的基本概念 算法的时间复杂度和空间复杂度 【知识框架】 【复习提示】 本章内容是数据结构概述,并不在考研大纲中。读者可通过对本章的学习,初步…...
读懂 Vue3 路由:从入门到实战
在构建现代化单页应用(SPA)时,Vue3 凭借其简洁高效的特性成为众多开发者的首选。 而 Vue3 路由(Vue Router)则是 Vue3 生态中不可或缺的一部分,它就像是单页应用的 “导航地图”,帮助用户在不同…...
Aws S3上传优化
上传大约 3.4GB 的 JSON 文件,zip算法压缩后约为 395MB,上传至 S3 效率优化,有一些优化方案可以提高上传速率。下面是几种可能的优化方式,包括选择压缩算法、调整上传方式、以及其他可能的方案。 方案 1. 选择更好的压缩算法 压…...
Python 数据智能实战 (8):基于LLM的个性化营销文案
写在前面 —— 告别群发轰炸,拥抱精准沟通:用 LLM 为你的用户量身定制营销信息 在前面的篇章中,我们学习了如何利用 LLM 增强用户理解(智能分群)、挖掘商品关联(语义购物篮)、提升预测精度(融合文本特征的流失预警)。我们不断地从数据中提取更深层次的洞察。 然而,…...
html:table表格
表格代码示例: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><!-- 标准表格。 --><table border"5"cellspacing&qu…...
2.maven 手动安装 jar包
1.背景 有的时候,maven仓库无法下载,可以手动安装。本文以pentaho-aggdesigner-algorithm-5.1.5-jhyde.jar为例。 2.预先准备 下载文件到本地指定位置。 2.1.安装pom mvn install:install-file \-Dfile/home/wind/tmp/pentaho-aggdesigner-5.1.5-jh…...
C++ unordered_set unordered_map
上篇文章我们讲解了哈希表的实现,这节尝试使用哈希表来封装unordered_set/map 1. unordered_set/map的框架 封装的过程实际上与set/map类似,在unordered_set/map层传递一个仿函数,用于取出key值 由于我们平常使用的都是unordered_set/map&…...
第37课 绘制原理图——放置离页连接符
什么是离页连接符? 前边我们介绍了网络标签(Net Lable),可以让两根导线“隔空相连”,使原理图更加清爽简洁。 但是网络标签的使用也具有一定的局限性,对于两张不同Sheet上的导线,网络标签就不…...
< 自用文 Texas style Smoker > 美式德克萨斯烟熏炉 从设计到实现 (第一部分:烹饪室与燃烧室)
原因: 没钱还馋! 但有手艺。 预计目标: 常见的两种偏置式烟熏炉(Offset Smoker) 左边边是标准偏置式(Standard Offset),右边是反向流动式(Reverse Flow Offset&#x…...
【现代深度学习技术】现代循环神经网络03:深度循环神经网络
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…...
AimRT从入门到精通 - 03Channel发布者和订阅者
刚接触AimRT的小伙伴可能会疑惑,这个Channel和RPC(后面讲的)到底是什么呢? 但是当我们接触了之后,就会发现,其本质类似ROS的Topic通信!(其本质基于发布订阅模型) 接下来…...
MySQL初阶:数据库基础,数据库和表操作,数据库中的数据类型
1.数据库基础 数据库是一个客户端——服务器结构的程序。 服务器是真正的主体,负责保存和管理数据,数据都存储在硬盘上 数据库处理的主要内容是数据的存储,查找,修改,排序,统计等。 关系型数据库&#…...
AI 驱动的智能交通系统:从拥堵到流畅的未来出行
最近研学过程中发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。下面开始对正文内容的…...
Python清空Word段落样式的方法
在 Python 中,你可以使用 python-docx 库来操作 Word 文档,包括清空段落样式。以下是几种清空段落样式的方法: 方法一:直接设置段落样式为"Normal" from docx import Documentdoc Document(your_document.docx) # 打…...
[javaEE]网络编程
目录 socket对tcp ServerSocket ServerSocket 构造方法: ServerSocket 方法: socket 实现回显服务器和客户端 由于我们之前已经写多了socket对udq的实现,所以我们这节,主要将重心放在Tcp之上 socket对tcp ServerS…...
组件通信-mitt
mitt:与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。 第一步:安装mitt npm i mitt 第二步:新建文件:src\utils\emitter.ts // 引入mitt import mitt from "mitt"; //调…...
微软发布了最新的开源推理模型套件“Phi-4-Reasoning
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
Socat 用法详解:网络安全中的瑞士军刀
Socat 用法详解:网络安全中的强大工具 引言 socat(SOcket CAT)是一款功能强大的命令行工具,被誉为“网络瑞士军刀”,广泛应用于数据传输、端口转发和网络调试等场景。它支持多种协议和数据通道(如文件、管…...
精益数据分析(36/126):SaaS商业模式的指标动态与实践案例
精益数据分析(36/126):SaaS商业模式的指标动态与实践案例 在创业与数据分析的学习道路上,我们不断探索各种商业模式的核心要点。今天,依旧怀揣着和大家共同进步的想法,深入研读《精益数据分析》中SaaS商业…...
2.LED灯的控制和按键检测
目录 STM32F103的GPIO口 GPIO口的作用 GPIO口的工作模式 input输入检测 -- 向内检测 output控制输出 -- 向外输出 寄存器 寄存器地址的确定 配置GPIO口的工作模式 时钟的开启和关闭 软件编程驱动 LED 灯 硬件 软件 软件编程驱动 KEY 按键 硬件 软件 按键消抖 代码 STM32F…...
架构师面试(三十八):注册中心架构模式
题目 在微服务系统中,当服务达到一定数量时,通常需要引入【注册中心】组件,以方便服务发现。 大家有没有思考过,注册中心存在的最根本的原因是什么呢?注册中心在企业中的最佳实践是怎样的?注册中心的服务…...
Go-web开发之帖子功能
帖子功能 route.go r.Use(middleware.JWTAuthMiddleware()){r.POST("/post", controller.CreatePostHandler)r.GET("/post/:id", controller.GetPostDetailHandler)}post.go 定义帖子结构 type Post struct {Id int64 json:"id" …...
MYSQL-设计表
一.范式 数据库的范式是⼀组规则。在设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数 据库,这些不同的规范要求被称为不同的范式。 关系数据库有六种范式:第⼀范式(1NF)、第⼆范式(…...
动态思维——AI与思维模型【91】
一、定义 动态思维思维模型是一种强调在思考问题和分析情况时,充分考虑到事物的变化性、发展性和相互关联性,不局限于静态的、孤立的视角,而是以发展变化的眼光看待事物,能够根据不同时间、环境和条件的变化,灵活调整…...
文献阅读篇#7:5月一区好文阅读,BFA-YOLO,用于建筑信息建模!(下)
期刊简介:《Advanced Engineering Informatics》创刊于2002年,由Elsevier Ltd出版商出版,出版周期Quarterly。该刊已被SCIE数据库收录,在中科院最新升级版分区表中,该刊分区信息为大类学科工程技术1区,2023…...
【Linux网络编程】http协议的状态码,常见请求方法以及cookie-session
本文专栏:Linux网络编程 目录 一,状态码 重定向状态码 1,永久重定向(301 Moved Permanently) 2,临时重定向(302 Found) 二,常见请求方法 1,HTTP常见Hea…...
ARM 指令集(ubuntu环境学习)第六章:ARM 编程技巧与优化策略
在本章中,我们将介绍一些在 ARM 架构上编写高效代码的技巧和常见优化策略,帮助您在嵌入式系统中获得更低延迟、更高吞吐和更低功耗。 6.1 寄存器利用与最小化内存访问 多用寄存器 ARM 通用寄存器(r0–r12)数量充足,尽量将临时变量保留在寄存器中,减少对内存的读写。 避免…...
柔性超声耦合剂的选择与设计-可穿戴式柔性超声耦合剂面临的难题
柔性PZT压电薄膜:破解可穿戴式超声耦合剂难题的关键材料! 随着可穿戴技术的快速发展,超声设备正朝着轻量化、柔性化和高集成度方向演进。在医学诊断、健康监测和智能穿戴领域,可穿戴式超声设备因其无创、实时、动态成像等优势受到…...
XCTF-pwn(二)
guess_num 看一下文件信息 利用gets函数将seed[0]给覆盖掉 距离0x20 我们需要输入十次随机数产生的值 写一个c程序先预判当seed是a的时候产生的随机数分别是多少 payload from pwn import* from ctypes import* context.log_leveldebugrremote("61.147.171.105", 6…...
AI外挂RAG:大模型时代的检索增强生成技术
目录 引言 一、RAG是什么? 二、RAG为什么会出现? 三、RAG的工作原理 四、RAG的技术优势 五、RAG的应用场景 六、RAG对AI行业的影响 七、RAG面临的挑战 引言 在人工智能领域,大型语言模型(LLM)如ChatGPT、DeepSe…...
SpringTask
Spring Task是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 应用场景:信用卡每月还款提醒、火车票售票系统处理未支付订单 fixedDelay:上一次任务执行完成后多长时间(ms)执行下一次任务 fixe…...
Sphinx 文档图片点击放大
文章目录 问题描述解决方案步骤 1:创建 JavaScript 文件步骤 2:编写 JavaScript 代码步骤 3:更新 Sphinx 配置 高级定制为所有图片添加点击功能添加缩放控制 总结 在使用 Sphinx 生成技术文档时,我们经常需要在文档中嵌入截图和示…...
菜鸟之路Day29一一MySQL之DDL
菜鸟之路Day29一一MySQL之DDL 作者:blue 时间:2025.5.2 文章目录 菜鸟之路Day29一一MySQL之DDL0.概述1.DDL之数据库操作1.1查询1.2创建1.3使用1.4删除 2.DDL之表操作2.1创建表2.2数据类型2.3查询表2.4修改表结构2.5删除表 0.概述 文章内容学习自黑马程…...
架构师面试(三十七):监控系统架构模式
题目 监控是在产品生命周期的运维环节,能对产品的关键指标数据进行【实时跟踪】并对异常数据进行【实时报警】。 一句话描述,监控系统可以帮我们【主动预防和发现】业务系统中的问题。 我们常说,监控系统是 “粮草”,业务系统是…...
【Redis】Hash哈希
文章目录 注意个问题hset命令(核心)hget命令(核心)hexists命令hdel命令hkeys和hvals命令hgetall和hmget命令hlen命令hsetnx命令hincrby命令哈希命令小结哈希编码方式使用场景1.关系型数据表保存用户的信息Redis三种缓存方式对比1.…...
【SpringBoot】Spring中事务的实现:声明式事务@Transactional、编程式事务
1. 准备工作 1.1 在MySQL数据库中创建相应的表 用户注册的例子进行演示事务操作,索引需要一个用户信息表 (1)创建数据库 -- 创建数据库 DROP DATABASE IF EXISTS trans_test; CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;…...