openharmony5.0中hdf框架中实现驱动程序的动态加载和管理的技术细节分析
在分析openharmony的hdf框架的设备驱动加载器(IDriverLoader)时发现在创建实例时会首先判断一下是否完成了驱动入口的构建(HdfDriverEntryConstruct),如果没有构建会重新构建,这与我开始以为的不一致(我一直以为是采用的linux内核方式,只是由hdf来将驱动注册进去),oh将这部分由hdf框架接管了。下面我便将这部分实现驱动程序的动态加载和管理的技术细节进行分析。
实现驱动程序的动态加载和管理主要过程如下:
- 驱动注册:通过
HDF_DRIVER_INIT宏将驱动入口点注册到.hdf.driver段。 - 驱动加载:由设备管理器解析配置文件并调用驱动的
Bind和Init函数。 - 服务发布与订阅:通过
HdfDeviceNodePublishService和HdfDeviceSubscribeService实现。 - 驱动卸载:通过
Release函数释放驱动资源。
在使用前首先看下驱动是如何应用的(有个大概的使用思路)
struct HdfDriverEntry g_i2cManagerEntry = {//驱动程序的入口信息.moduleVersion = 1,//驱动模块的版本号.Bind = I2cManagerBind,//用于将驱动与设备对象关联.Init = I2cManagerInit,//用于完成驱动的初始化操作.Release = I2cManagerRelease,//用于释放驱动占用的资源.moduleName = "HDF_PLATFORM_I2C_MANAGER", //必须与 device_info.hcs 文件中的设备名称一致
};
HDF_INIT(g_i2cManagerEntry);//用于将驱动程序的入口结构注册到 HDF 框架中
下面分别介绍动态加载和管理的四个主要过程。
驱动注册
在 OpenHarmony 中,驱动程序通过 HDF_INIT宏注册到 .hdf.driver 段中。这个宏将驱动程序的入口点信息存储到特定的内存段,供运行时加载。
在hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h文件中我们可以看到实际HDF_INIT宏实际调用的为HDF_DRIVER_INIT。
#define HDF_INIT(module) HDF_DRIVER_INIT(module)
- HDF_DRIVER_INIT说明
结合hdf_core\framework\core\common\include\host\hdf_device_section.h代码的宏定义
//这用于确保驱动程序的入口点不会被编译器优化掉
#define USED_ATTR __attribute__((used))//用于将变量或函数放置到指定的内存段(section)中
//将驱动程序的入口点信息存储到 .hdf.driver 段中
//这个段在运行时会被 HDF 框架扫描,以获取驱动程序的入口点
#define HDF_SECTION __attribute__((section(".hdf.driver")))
可得注册一个驱动程序模块
#define HDF_DRIVER_INIT(module) \const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))
定义一个全局变量(module##HdfEntry),其名称由驱动模块名(module)拼接而成。使用 USED_ATTR 确保该变量不会被优化掉。使用 HDF_SECTION 将该变量放置到 .hdf.driver 段中。将该变量的值设置为驱动模块的地址(&(module))。这样,驱动程序的入口点信息就会被存储到 .hdf.driver 段中,供运行时扫描和加载。
此时便有个疑问,我们只知道驱动程序的入口点信息就会被存储到 .hdf.driver 段中,那我们如何访问或使用它呢?
答案就在hdf_core\framework\core\common\include\host\hdf_device_section.h文件中的以下两个函数中
/*** 这个宏定义用于获取指定变量的地址。* 声明一个外部变量(extern type name)。* 返回该变量的地址(&(name))。* 这个宏主要用于定义 _hdf_drivers_start 和 _hdf_drivers_end,标记驱动程序入口点的范围。*/
#define HDF_DRIVER_SEC_NAME(type, name) \({extern type name; \&(name); \})/**通过 HDF_DRIVER_SEC_NAME 宏,获取 _hdf_drivers_start 和 _hdf_drivers_end 的地址。
这些地址在运行时被 HDF 框架用来遍历 .hdf.driver 段中的驱动程序入口点。
HDF 框架可以通过 _hdf_drivers_start 和 _hdf_drivers_end 之间的范围,动态加载所有注册的驱动程序。
*/
#define HDF_DRIVER_BEGIN() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_start)
#define HDF_DRIVER_END() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_end)
经过对上述代码的理解我们知道如何来使用了,但是此时还有一个疑问没有解决,**那就是_hdf_drivers_start和_hdf_drivers_end变量在哪定义的呢?它在哪被赋值了呢?**通过对代码的搜索可以看到在链接器脚本(vmlinux.lds.S)中有相关定义。如下图所示:

这段代码的作用是:
- 定义一个名为
.init.hdf_table的段。 - 在该段的起始位置设置
_hdf_drivers_start符号。 - 将所有标记为
.hdf.driver的数据(即驱动程序入口点)放入该段。 - 在该段的结束位置设置
_hdf_drivers_end符号。
这部分的内容有点绕为了更好的总结在这儿弄了个使用demo,可以供参考理解:
// 定义一个驱动程序模块
struct HdfDriverEntry MyDriver;// 注册驱动程序模块
HDF_DRIVER_INIT(MyDriver);// 在代码中使用 HDF_DRIVER_BEGIN 和 HDF_DRIVER_END 获取驱动程序范围
size_t *start = HDF_DRIVER_BEGIN();
size_t *end = HDF_DRIVER_END();// 遍历驱动程序入口点
for (size_t *ptr = start; ptr < end; ptr++) {struct HdfDriverEntry *driver = (struct HdfDriverEntry *)(*ptr);// 加载驱动程序
}
驱动加载
驱动加载过程由 HDF 框架的设备管理器(Device Manager)和设备主机(Device Host)协同完成,这部分详细的过程比较复杂(本周应该能写完这篇汇总的),此处只列出核心点:
-
设备管理器初始化:
static int __init DeviceManagerInit(void) {int ret = DeviceManagerStart();if (ret < 0) {HDF_LOGE("%s start failed %d", __func__, ret);} else {HDF_LOGD("%s start success", __func__);}return ret; } late_initcall(DeviceManagerInit);设备管理器在系统启动时通过
late_initcall初始化,并开始加载驱动。 -
驱动加载流程:
- 设备管理器解析配置文件(
device_info.hcs),获取设备和驱动的映射关系。 - 根据配置文件中的
preload字段,决定驱动是按需加载还是按序加载。 - 遍历
.hdf.driver段,获取驱动程序的入口地址。 - 调用驱动的
Bind和Init函数完成初始化:
- 设备管理器解析配置文件(
代码调用过程如下:

int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode)
{const struct HdfDriverEntry *driverEntry = devNode->driver->entry;int ret = DeviceDriverBind(devNode);if (ret != HDF_SUCCESS) {return ret;}ret = driverEntry->Init(&devNode->deviceObject);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_DEV_INIT_FAIL;}ret = HdfDeviceNodePublishService(devNode);if (ret != HDF_SUCCESS) {return HDF_DEV_ERR_PUBLISH_FAIL;}return ret;
}
服务发布与订阅
HDF 框架支持服务发布和订阅机制,允许驱动程序在初始化后发布服务接口,供其他模块使用
-
服务发布:
int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode); -
服务订阅:
其他模块可以通过 HdfDeviceSubscribeService 订阅驱动服务。当驱动加载完成后,框架会将服务接口发布给订阅者
int HdfDeviceSubscribeService(struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback);
对此部分内容需要详细了解的可以参考
驱动卸载
和驱动加载相对应的卸载驱动的函数为HdfDeviceUnlaunchNode,当驱动被卸载或初始化失败时,HDF 框架会调用驱动的 Release 函数释放资源:
static void HdfDeviceUnlaunchNode(struct HdfDeviceNode *devNode)
{....if (devNode->driver != NULL) {driverEntry = devNode->driver->entry;//从设备节点中获取驱动程序对象(devNode->driver)}if (driverEntry != NULL && driverEntry->Release != NULL) {driverEntry->Release(&devNode->deviceObject);//如果驱动程序的入口结构和 Release 函数指针都有效,调用 Release 函数释放驱动占用的资源,即驱动中传入的release函数}....
}
相关文章:
openharmony5.0中hdf框架中实现驱动程序的动态加载和管理的技术细节分析
在分析openharmony的hdf框架的设备驱动加载器(IDriverLoader)时发现在创建实例时会首先判断一下是否完成了驱动入口的构建(HdfDriverEntryConstruct),如果没有构建会重新构建,这与我开始以为的不一致(我一直以为是采用的linux内核方式,只是由…...
TVS管学习记录
文章目录 前言一、TVS是什么?二、TVS关键参数1.反向截至电压**实际意义** 2.钳位电压**定义与作用****选择依据** **4. 实际应用示例****场景:通信端口的ESD保护** 3.反向截至电压和钳位电压的关联和区别**. 小结** 三、实际应用电路举例总结 前言 TVS管…...
数据库表的各种设计
本篇文章,主要讲解项目开发时,遇到不同的情况,要学会对数据库的表进行合理设计。 1、将表的某个字段,存到一张新表中 ①情况描述 ②操作步骤 第一步:创建role表 第二步:在user表中,删除role字…...
JWT使用教程
目录 JWT (JSON Web Token)1. JWT简介(1) 什么是JWT(2) JWT有什么用(3) JWT认证方式 2. JWT的组成部分3. 签名的目的4. JWT与Token的区别5 JWT的优势6 JJWT签发与验证token(1) 引入依赖(2) 创建 Token(3) 解析Token(4) 设置过期时间(5) 自定义claims 7. JWT自定义工具类 JWT (J…...
【大模型系列篇】如何解决DeepSeek-R1结构化输出问题,使用PydanticAl和DeepSeek构建结构化Agent
今日号外:🔥🔥🔥 DeepSeek开源周:炸场!DeepSeek开源FlashMLA,提升GPU效率 下面我们开始今天的主题,deepseek官方明确表示deepseek-r1目前不支持json输出/function call,可…...
老旧android项目编译指南(持续更)
原因 编译了很多项目,找到了一些可观的解决办法 1. android studio里面的jdk版本切换 jdk版本切换在这里,一般安卓开发需要用到4个版本的jdk,jdk8, jdk11, jdk17, jdk21新版的android stuio是默认使用高版本的jdk,所以切换版本是很有必要的 2. 命令…...
linux中安装部署Jenkins,成功构建springboot项目详细教程
参考别人配置Jenkins的git地址为https,无法连上github拉取项目,所以本章节介绍通过配置SSH地址来连github拉取项目 目录: 1、springboot项目 1.1 创建名为springcloudproject的springboot项目工程 1.2 已将工程上传到github中,g…...
AI开发利器:Anaconda
在Python开发过程中,不同的项目可能会依赖不同版本的Python以及各种不同版本的库。比如,项目A可能依赖Python 3.8和某个特定版本的numpy、TensorFlow和PyTorch,而项目B可能需要Python 3.9以及另一个版本的numpy库。如果直接在系统中安装Pytho…...
java网络编程--基于TCP协议的网络编程
Scoket介绍 利用 TCP 协议进行通信的两个应用程序是有主次之分的, 一个是服务器程序,一个是客户端程序, 两者的功能和编写方法不太一样, 其中 ServerSocket 类表示 Socket 服务器端,Socket 类表示 Socket 客户端。 服…...
PageHelper新发现
PageHelper 背景解决reasonablepageSizeZero 背景 今天发现了一个很有趣的现象,接手一个很老的项目springmvc项目、使用PageHelper分页实现常见的后端接口分页功能。但是发现当页码参数大于实际的页码数时、正常不应该返回数据,但是目前确一直返回数据不…...
Redis 数据结构和使用详解(带示例)
Redis 支持的主要数据结构及其对应操作命令的详细解释,结合具体使用场景和示例: 1. 字符串(String) 用途:存储文本、数值或二进制数据,适用于缓存、计数器等。 常用命令: SET key value&#x…...
深度解读 Chinese CLIP 论文:开启中文视觉对比语言预训练
目录 论文概述1.论文摘要2.论文脑图3.论文创新3.1模型构建3.2训练方法3.3数据构建3.4部署优化 4.模型架构 论文解析1. 引言2. 方法2.1数据说明2.2预训练方法2.2.1模型初始化方法2.2.2两阶段预训练方法 2.3预训练细节2.3.1模型初始化2.3.2第一阶段预训练2.3.3第二阶段预训练2.3.…...
SpringBoot 2 后端通用开发模板搭建(异常处理,请求响应)
目录 一、环境准备 二、新建项目 三、整合依赖 1、MyBatis Plus 数据库操作 2、Hutool 工具库 3、Knife4j 接口文档 4、其他依赖 四、通用基础代码 1、自定义异常 2、响应包装类 3、全局异常处理器 4、请求包装类 5、全局跨域配置 补充:设置新建类/接…...
【Oracle专栏】sqlplus显示设置+脚本常用显示命令
Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.内容概述 本文主要针对oracle 运维中常用知识点进行整理,包括: 1)sqlplus模式下,为了方便查询设置相应的行宽、列宽、行数。…...
DeepSeek 助力 Vue3 开发:打造丝滑的页眉(Header)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
JVM线程分析详解
java线程状态: 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建…...
【备赛】点亮LED
LED部分的原理图 led前面有锁存器,这是为了防止led会受到lcd的干扰(lcd也需要用到这些引脚)。 每次想要对led操作,就需要先打开锁存器,再执行操作,最后关闭锁存器。 这里需要注意的是,引脚配置…...
【音视频】编解码相关概念总结
NALU RTP PS流 三者总体关系 NALU在RTP中的应用:视频流的RTP传输通常将NALU作为基本的单元进行传输。每个RTP包携带一个或多个NALU,这些NALU包含了视频编码数据。RTP协议通过其头部信息(如时间戳、序列号等)帮助接收端重新排列和…...
Python爬虫(四)- Selenium 安装与使用教程
文章目录 前言一、简介及安装1. Selenium 简介2. Selenium 安装 二、Selenium 基本使用1. 导入Selenium2. 启动浏览器3. 打开网页4. 获取页面标题5. 关闭浏览器6. 完整示例代码 三、Selenium WebDriver1. 简介2. 基本操作2.1 启动浏览器2.2 关闭浏览器2.3 打开网页2.4 关闭当前…...
Node.js项目启动流程以及各个模块执行顺序详解
Node.js项目启动流程以及各个模块执行顺序的问题。首先,我需要仔细阅读并理解我搜索到的资料,从中提取关键信息,然后综合这些信息组织成一个结构化的回答。 首先,根据我搜索到的资料都详细描述了Node.js的启动流程,涉及…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
