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

RT-thread 链接阶段如何把段排列到内存里,然后运行阶段如何遍历这些函数指针并调用。

目录

1.编译的时候如何将函数定义到相同等级的段里面

2.链接阶段如何把段排列到内存里,然后运行阶段如何遍历这些函数指针并调用。

3.start/end 边界的作用

4.运行阶段如何遍历调用

5.不同等级的调用阶段

6.最后总结自动初始化机制的意义


1.编译的时候如何将函数定义到相同等级的段里面

定义一个函数指针变量,把初始化函数 fn 的地址保存进去,并把这个变量放到指定的链接段 .rti_fn.xxx 中。

#define INIT_EXPORT(fn, level) \ rt_used const init_fn_t __rt_init_##fn rt_section(".rti_fn." level) = fn

INIT_EXPORT(my_can_init, "3");

宏展开

rt_used const init_fn_t __rt_init_my_can_init rt_section(".rti_fn." "3") = my_can_init;

问题:它放在 .rti_fn.0 段的后面、.rti_fn.1 段的前面,用来作为 board 初始化阶段的起始边界。

static int rti_board_start(void){ return 0;}INIT_EXPORT(rti_board_start, "0.end");

为什么放在它放在 .rti_fn.0 段的后面、.rti_fn.1 段的前面呢?

keil编译,链接器会将输入段按特定顺序防止在执行区域内,

各章节按顺序排列:

1.只读代码

2.只读数据

3.读写代码

4.读写数据

5.零初始化数据

如果输入的节名称具有相同的属性,则进行比较。名称区分大小写,并按 ASCII 字符排序规则的字母顺序进行比较。

那么使用GCC 编译的话需要自己去定义linker script?

Keil/ARM 编译工具链: ARMCC 或 ARMCLANG + armlink + scatter 文件 GCC 编译工具链: arm-none-eabi-gcc + arm-none-eabi-ld + linker script

GCC:靠 linker script 里的 SORT(.rti_fn*)

如果用的是GCC 编译,那么就需要去在工程中查看link.lds 文件,通常会定义好编译规则顺序

Keil:靠 scatter 文件里的 .ANY(+RO) 收集 RO 段,然后 armlink 按段属性和段名排序放置

.ANY (+RO) 为什么仅仅一个这个符号在.sct 文件下,就可以实现编译的时候把这些段的信息放在一起呢?

.ANY(+RO) 是 Keil ARM Linker 的 scatter 文件语法,意思是:

把所有还没有被其他规则匹配走的只读输入段,统一放到当前执行区域里。

把所有没有特殊指定位置的只读段,都放到 ER_IROM1 这个 Flash 代码区里。

rt_used const init_fn_t __rt_init_xxx rt_section(".rti_fn.x") = xxx;

能放到 .rti_fn.x 这个指定段里,关键靠的是 rt_section(".rti_fn." level),也就是编译器的 section 属性。

如果没有 rt_section 会怎样?

它大概率会被编译器放到普通只读数据段,比如:

.rodata .constdata

2.链接阶段如何把段排列到内存里,然后运行阶段如何遍历这些函数指针并调用。

在编译阶段,每个使用 INIT_EXPORT 修饰的初始化函数,都会生成一个函数指针变量,并通过 rt_section(".rti_fn.x") 放入指定的输入段中。

在 Keil 工程中,链接器会根据 scatter 文件中的 .ANY(+RO) 规则,将这些只读输入段统一收集到 Flash 的只读执行区域中。并且是连续的地址下。

3.start/end 边界的作用

static int rti_board_start(void) { return 0; } INIT_EXPORT(rti_board_start, "0.end"); static int rti_board_end(void) { return 0; } INIT_EXPORT(rti_board_end, "1.end");

为了让系统知道某一类初始化函数的起始地址和结束地址,RT-Thread 定义了若干个空函数作为边界标记。

这些函数本身不完成实际初始化工作,它们的主要作用是生成一个确定的符号地址。

例如

低地址 ↓ __rt_init_rti_board_start -> .rti_fn.0.end __rt_init_gpio_init -> .rti_fn.1 __rt_init_uart_init -> .rti_fn.1 __rt_init_can_init -> .rti_fn.1 __rt_init_rti_board_end -> .rti_fn.1.end 高地址

rti_board_start 放在 "0.end",不是因为它表示 board 的结束,而是因为它要排在 .rti_fn.1 之前,用来作为 board 初始化函数区间的起始边界。

4.运行阶段如何遍历调用

运行阶段如何遍历调用?

遍历函数指针表的方式调用。

fn_ptr 从 __rt_init_rti_board_start 开始,依次向后移动,每移动一次就取出一个函数指针并执行,直到到达 __rt_init_rti_board_end 为止。

typedef int (*init_fn_t)(void); volatile const init_fn_t *fn_ptr; for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++) { (*fn_ptr)(); }

5.不同等级的调用阶段

board 阶段主要用于硬件相关初始化,例如 CPU、时钟、中断控制器、GPIO、串口、CAN 等。这些初始化需要在系统线程调度启动之前完成。

当内核调度器启动后,RT-Thread 会创建初始化线程 init_thread,在该线程中继续执行设备、组件、环境和应用层初始化。

这样可以将底层硬件初始化和上层软件组件初始化分开,保证系统启动顺序清晰。

6.最后总结自动初始化机制的意义

RT-Thread 自动初始化机制的核心思想是:

通过宏定义在编译阶段生成函数指针变量,通过 section 属性将函数指针放入指定初始化等级的段中;链接阶段再由 scatter 文件将这些段统一放入 Flash 只读区域;运行阶段系统根据 start/end 边界符号遍历函数指针表,从而自动调用各个初始化函数。

http://www.jsqmd.com/news/718075/

相关文章:

  • 字符缩到0.8mm板子丑到没法看!忽略的丝印美学
  • mini-job极简分布式延迟任务队列 — 基于 Redis,支持 Cron 周期任务、异步协程和多执行器
  • 【论文阅读】AWR:Simple and scalable off-policy RL
  • AI 赋能研发:现代开发者的效率进阶与工程化落地实践
  • 思源黑体TTF:7种字重完美解决多语言排版难题
  • 二向箔压缩测试:从宇宙规律武器到软件测试范式的跨界思考
  • AWS DevOps Agent 实测:AI 自主运维从告警到根因报告的完整技术路径
  • 【Hot 100 刷题计划】 LeetCode 23. 合并 K 个升序链表 | C++ 顺序合并
  • MusicFree插件完全指南:打造你的个性化跨平台音乐中心
  • 推荐2款无需安装实用软件,桌面图标整理设置,简真是Windows神器!
  • 解码AI用户心智,筑牢可信GEO根基——悠易科技深度参与《中国AI用户态度与行为研究报告(2026)》发布会
  • 从Jupyter Notebook到生产API,Docker AI Toolkit 2026全流程自动化部署(含OpenTelemetry埋点、Prometheus监控集成脚本)
  • GitHub中文界面大改造:3分钟让英文GitHub秒变中文版
  • XPath Helper Plus:3分钟掌握网页元素精准定位的终极指南
  • WASM容器化部署为何突然爆发?,2026全球Top 12边缘AI项目验证的Docker+WASI运行时架构演进路径
  • 别再为低价忽视丝印规格
  • 如何3分钟解锁Wallpaper Engine所有壁纸素材?RePKG工具终极指南
  • Ostrakon-VL-8B数据预处理详解:餐饮图像清洗与标注规范
  • 从ArrayList到VectorSpecies:Java向量化开发全流程拆解,含GraalVM AOT+Linux perf火焰图调优实战
  • MCP Server 接口开发规范与最佳实践
  • QQ音乐加密文件终极解密指南:3步解锁你的音乐宝藏
  • 忍者像素绘卷Codex使用技巧:利用AI编程助手快速开发模型调用脚本
  • Java 25虚拟线程资源调度黄金参数表(2024 Q3压测实录:TPS提升3.8倍,P99延迟下降67ms)
  • Gmail账号自动生成神器:Python脚本实现3分钟批量创建无限邮箱
  • 构建基于nli-MiniLM2-L6-H768的智能学习系统:习题与知识点自动关联
  • WeDLM-7B-Base入门:Python零基础环境配置与第一个生成程序
  • 一次惊心动魄的年报
  • 程序验证技术演进与Preguss框架创新实践
  • 【基于 macOS 虚拟机的 iMessage 批量消息处理技术实践】
  • 数据结构基础------初识二叉树