RK3568驱动指南|驱动基础进阶篇-进阶1 编译进内核的驱动系统是如何运行的?

news2024/7/6 19:38:22

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。

动基础-进阶篇

进阶1 编译进内核的驱动系统是如何运行的?

在经过前面章节的学习后,相信大家已经对驱动有了一些自己的认识和理解,从本章开始将对一些驱动相关的进阶知识进行讲解。本章要研究的内容为编译进内核的驱动系统是如何运行的?

在驱动程序中,module_init 宏定义了驱动的入口函数,在模块加载时被内核自动调用,该宏定义在内核源码目录下的“include/linux/module.h”文件中,具体内容如下所示:

module_init的具体内容由MODULE宏定义来决定,该宏定义在内核源码的顶层Makefile中,具体为KBUILD_CFLAGS_KERNEL和KBUILD_CFLAGS_MODULE两个宏,如下所示:

由于本章节探究的是编译进内核的驱动,所以要看KBUILD_CFLAGS_KERNEL宏定义,该宏为空,那module_init 的宏定义具体内容如下所示:

注意:因为静态编译的驱动无法卸载,所以module_exit在编译进内核的驱动中并不会被执行!所以这里只是分析module_init。

然后继续向下查找__initcall的定义路径,该宏定义在内核源码目录下的“include/linux/init.h”文件中,具体内容如下所示:

#define __initcall(fn) device_initcall(fn)

接下来会发现该宏定义仍会套很多层宏定义,这些宏都在内核源码目录下的“include/linux/init.h”文件中,具体后续嵌套内容如下所示:

由于嵌套关系较为复杂,这里以module_init(helloworld)为例绘制了调用关系,具体内容如下所示:

注意:##代表强制连接,#表示对这个变量替换后,用双引号引起来。

而宏定义展开到最后的initcall_t是一个函数指针,它的原型如下所示:

typedef int (*initcall_t)(void);

所以,当使用module_init(helloworld)宏定义模块的入口函数后,会创建一个 __initcall_hello_world6函数指针变量,并将其初始化为hello_world函数,这个__initcall_hello_world6函数指针变量的目的是将模块的入口函数放置在内核的初始化调用链中,以便在系统引导期间自动执行。

在编译过程中,这个函数指针会被放置在.initcall6.init段中。这个段是内核初始化调用链的一部分,用于在系统引导期间按顺序调用所有位于该段中的函数。通过将模块的入口函数放置在.initcall6.init段中,可以确保在系统引导期间自动调用该函数,从而初始化模块并注册模块的功能。

而在内核源码中除了module_init,还有其他的宏定义接口用来完成初始化模块并注册模块的功能,他们的原型都是define_initcall,只是相应的优先级不同,而优先级的不同就导致了系统启动时驱动模块的加载先后顺序不一样,module_init的优先级是6,其他的宏定义在include/linux/init.h 文件中,具体内容如下所示:

而在include/asm-generic/vmlinux.lds.h 链接脚本(linker script)中定义初始化调用函数的布局和顺序,具体内容如下所示:

NIT_CALLS_LEVEL(level) 宏用于定义特定优先级(level)的初始化调用函数的布局。它会创建以下两个符号:

__initcall[level]_start:表示该优先级初始化调用函数段的起始位置。

__initcall[level]s_start:表示该优先级初始化调用函数段的起始位置(用于静态初始化)。

接着,INIT_CALLS宏用于定义整个初始化调用函数的布局。它按照一定的顺序将不同优先级的初始化调用函数放置在链接器脚本的相应位置。具体的步骤如下:

1.定义 __initcall_start符号,表示初始化调用函数段的起始位置。

2.使用KEEP命令保留所有.initcallearly.init段中的内容。这个段包含了一些早期的初始化调用函数,它们会在其他优先级之前被调用。

3.依次调用 INIT_CALLS_LEVEL 宏,传入不同的优先级参数,将相应优先级的初始化调用函数放置在链接器脚本中的正确位置。

4.定义 __initcall_end 符号,表示初始化调用函数段的结束位置。 

链接器在链接过程中会根据这些符号的位置信息,将初始化调用函数按照优先级顺序放置在对应的段中。这样,当系统启动时,初始化调用函数将按照定义的顺序被调用,实现系统的初始化和功能注册。

展开之后的INIT_CALLS宏内容如下所示:

_initcall0_start等以_start结尾的相关变量记录了.initcall0.init等段的首地址,这些变量在 init/main.c中通过extern关键字进行引用,并将这些首地址放置在数组initcall_levels中,具体内容如下所示:

在1-10行声明了一系列__initcall0_start相关变量,在第12行定义了一个名为initcall_levels 的静态指针数组。该数组用于存储不同优先级的初始化调用函数段的起始地址。数组的元素对应不同的优先级,按照顺序存储了对应优先级的起始地址。该数组最终会在do_one_initcall函数中执行,由于调用关系较为复杂,所以这里直接绘制出了相应的调用关系图,具体内容如下所示:

首先来看do_initcalls函数,该函数定义在内核源码的init/main.c目录下,具体内容如下所示:

在第9行,循环遍历了initcall_levels数组,其中ARRAY_SIZE(initcall_levels)表示 initcall_levels数组的大小,do_initcalls函数的循环将执行7次do_initcall_level。在每次循环中,do_initcall_level函数被调用,并传递当前迭代的level值作为参数,数字越小,优先级越高,带s段的优先级要小于不带 "s" 段的优先级,然后我们继续来看do_initcall_level函数,该函数的具体内容如下所示:

在该函数中最重要的内容为24、25行的for循环,关于for循环内容的具体解释如下所示:

(1)fn 是一个指向initcall_entry_t类型的指针,用于迭代遍历当前级别的初始化调用函数数组。

(2)initcall_levels[level] 表示当前级别的初始化调用函数数组的起始地址。

(3)initcall_levels[level+1] 表示下一个级别的初始化调用函数数组的起始地址。由于数组是连续存储的,因此通过比较fn和initcall_levels[level+1]的值,可以确定循环的终止条件。

(4)do_one_initcall是一个函数,用于执行单个初始化调用函数。它接受一个函数指针作为参数,并调用该函数。

(5)initcall_from_entry是一个宏,用于从函数指针fn中获取实际的初始化调用函数。

因此,循环的作用是遍历当前级别的初始化调用函数数组,并依次将每个函数指针传递给 do_one_initcall 函数执行初始化调用。通过这个循环,可以按照预定义的顺序执行每个初始化调用函数,完成系统的初始化过程。do_one_initcall函数的具体内容如下所示:

该函数的作用是执行单个初始化调用函数并处理相关逻辑,至此一系列的调用关系就解释完成了。

最后对本章节内容进行一下简单的总结,在使用module_init(hello_world)时,hello_world()函数指针会被放置在.initcall6.init段处。内核启动时,会执行do_initcall()函数,该函数根据指针数组initcall_levels[6]找到_initcall6_start,在include/asm-generic/vmlinux.lds.h文件中可以查到_initcall6_start对应.initcall6.init段的起始地址。然后,依次取出该段中的函数指针,并执行这些函数。

至此,关于编译进内核的驱动系统是如何运行的这一问题就讲解完成了,最后布置一个课程作业,利用本章节学习到的知识来让驱动可以更快的被加载,会在下一章中对该作业进行讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1426973.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现排序算法

目录 一&#xff1a;快速排序 二&#xff1a;合并排序 三&#xff1a;冒泡排序 四&#xff1a;插入排序 五&#xff1a;选择排序 一&#xff1a;快速排序 def quicksort(arr): if len(arr) < 1: return arr pivot arr[len(arr) // 2] le…

Unity | 渡鸦避难所-9 | 角色名字及血条等信息

1 效果预览 游戏中角色的名字和血条是非常重要的元素&#xff0c;它们可以帮助玩家了解角色的身份和状态。在 Unity 中&#xff0c;可以使用 UGUI 来实现这些功能 2 实现方案 1 画布 (Canvas) 画布 (Canvas) 组件表示进行 UI 布局和渲染的抽象空间。所有 UI 元素都必须是附加…

UE5 虚幻游戏报错常用解决方法(幻兽帕鲁UE5报错)

在体验使用虚幻引擎5、4&#xff08;UE5/UE4&#xff09;开发的游戏如《幻兽帕鲁》时&#xff0c;玩家可能会遇到各种报错情况&#xff0c;例如黑屏、闪退、C运行时错误等。本博客将汇集一系列有效解决方案&#xff0c;通过调整虚幻引擎内置命令行参数以及优化系统环境&#xf…

环状热力图R语言画法

环状热力图&#xff08;Circular Heatmap&#xff09;是一种以环状布局展示数据的可视化方法。它结合了热力图和极坐标系统&#xff0c;能够有效地显示数据的关系、模式和趋势。 环状热力图通常用于可视化二维数据矩阵&#xff0c;其中行和列代表不同的类别或变量&#xff0c;…

“欢天喜地迎新春”下姜村邻里守望写对联活动

卯兔追冬去&#xff0c;辰龙报春来。空谷幽香谱佳期&#xff0c;红联金句寄吉祥。春联是我国特有的文学形式&#xff0c;贴春联是继承传统习俗的一种方式&#xff0c;是对祖先的尊敬&#xff0c;对传统的继承。春节前夕&#xff0c;家家户户贴上红红的春联&#xff0c;一副副透…

前妻(C#)-基础03-枚举-预处理指令

前妻C#-基础语法03 枚举关于控制台IO及注释C#预处理指令 枚举 枚举是用户定义的整数类型。在声明一个枚举时&#xff0c;要指定改枚举的实例可以包含的一组可接受的值。不仅如此&#xff0c;还可以给值指定易于记忆的名称&#xff0c;如果在代码的某个地方&#xff0c;要试图把…

【SpringCloud】使用OpenFeign进行微服务化改造

目录 一、需求与背景二、OpenFeign 远程调用技术原理三、项目代码演示3.1 引入依赖3.2 实现OpenFeign注解修饰接口3.3 指定 OpenFeign 远程调用接口的扫描路径 四、OpenFeign 在日志中打印Request和Response五、OpenFeign 客户端超时配置六、使用 OpenFeign 实现服务降级6.1 实…

QT SQL

QT SQL模块提供数据库编程的支持&#xff0c;支持多种常见的数据库&#xff1a;MySQL\Oracle\MS SQL Server\SQLite等。SQL模块包含多个类&#xff0c;可以实现&#xff1a;数据库连接、SQL语句执行、数据获取与界面显示 等功能。数据 与 界面间用Model\View架构。 一、 二、Q…

禅道列表页编辑页添加页自定义字段

1&#xff0c;数据库表 zt_story 添加自定义字段 bakDate1&#xff0c;bakDate2&#xff0c;bakDate3&#xff0c;bakDate4 2&#xff0c;在 /opt/lampp/htdocs/zentaopms/extension/custom/story/ext/config 中添加bakDate.php文件 <?php $config->story->datatab…

【2024美国大学生数学建模竞赛】2024美赛C题网球运动中的势头,网球教练4.0没人比我更懂这个题了!!!

【2023美国大学生数学建模竞赛】2024美赛C题 问题分析、数学模型、实现代码、完整论文 引言 本人是计算机博士&#xff0c;拥有10年网球球龄&#xff0c;2023年的温网决赛&#xff0c;熬夜到半夜全称观看完了直播&#xff0c;对于网球规则、比赛的数据非常熟悉&#xff0c;这个…

【代码随想录-链表】环形链表 II

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

windows下使用verdaccio构建npm私服环境

一.背景 npm太慢了&#xff0c;每次jenkins自动构建等太久&#xff0c;我虽然是后端coder&#xff0c;也看不惯。本文目的只是说明怎么搭建npm私服&#xff0c;我现在只想构建快一点。所以&#xff0c;暂时没有考虑多个开发者将自定义组件上传到私库并共享的问题&#xff0c;以…

Linux中判断文件系统的方法

文章目录 Linux中判断文件系统的方法1.使用mount命令2.使用blkid命令3.使用file命令4.使用fstab文件5.使用df命令&#xff08;这个用的比较多&#xff09;6.使用fsck命令7.使用lsblk命令(推荐-简单好用) Linux中判断文件系统的方法 1.使用mount命令 # 这样查看的只有已经挂载…

Python系列-字典

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” ​ 目录 ​ 字典是什么 创建字典 查找key 新增/修改元素 删除元素 遍历字典元素 取出所有的key和value 合成的key类型 ​编辑 小结 字典是什么 字典是一种存储键值对的结…

D2632——LDO系列电路,高精度、大电流、低压差。最大输出电流可达3A,应用于汽车电子设备等产品中

D2632是一款高精度、大电流、低压差电压调整器。主要作为电源装置提供高效的电压调整。 最大输出电流可达3A&#xff0c;并且外接器件少&#xff0c;拥有输出电压(ADJ) 可调特点。 主要特点&#xff1a; 1. 低压差(满载350mV); 2. 地电流小; 3. 精确度保证在士1%; 4. 非常快速…

C++中的字符串翻转算法解析

个人主页&#xff1a;[PingdiGuo_guo] 收录专栏&#xff1a;[C干货专栏] 大家好&#xff0c;今天我们来学一下C里的一个知识&#xff1a;字符串翻转。 目录 1.题目 描述 输入描述 输出描述 输入数据 1 输出数据 1 提示 2.解决题目 1.所需知识点 2.算法分析 1. 拼接…

【PostgresSQL系列】 ltree简介及基于SpringBoot实现 ltree数据增删改查

本文将对PostgresSQL中的ltree进行相关概念介绍&#xff0c;并以示例代码讲解ltree数据增删改查功能的实现。 作者&#xff1a;后端小肥肠 目录 1.前言 2. 基础概念 2.1. ltree 2.2. lquery 2.3. ltxtquery 2.4. ltree函数及操作符 2.4.1. ltree函数 2.4.2. ltree操作符…

AJAX-认识URL

定义 概念&#xff1a;URL就是统一资源定位符&#xff0c;简称网址&#xff0c;用于访问网络上的资源 组成 协议 http协议&#xff1a;超文本传输协议&#xff0c;规定浏览器和服务器之间传输数据的格式 协议范围&#xff1a;http,https,... 域名 域名&#xff1a;标记服务…

2024年美赛数学建模F题思路分析 - 减少非法野生动物贸易

# 1 赛题 问题F&#xff1a;减少非法野生动物贸易 非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数据驱动的5年项目&#xff0c…

一键部署自己的chatgpt4

效果 安装 docker pull hlohaus789/g4f docker run -d -p 10036:8080 -p 1337:1337 -p 7900:7900 --shm-size"2g" hlohaus789/g4f:latest镜像比较大,大约1.82G 使用 浏览器打开 http://192.168.168.111:10036/ 愉快地使用吧