开源RT线程RTOS自动初始化机制分析
RT-Thread是一个开源的嵌入式实时操作系统,具有丰富的中间层组件和出色的硬件和软件生态系统,所有这些都为物联网行业提供了出色的支持。自 2006 年成立以来,RT-Thread 已为 1 亿台设备提供支持,包括可穿戴设备、智能家电、汽车电子、医疗电子、消费电子、能源和许多其他行业。
常规初始化
在嵌入式开发过程中,我们主要采用这种方法来初始化外设。
int main(int argc, char *argv[])
{
clk_init();
led_init();
beep_init();
key_init();
.....
while(1)
{
...
}
}
这种初始化的顺序相对清晰,很容易弄清楚已经初始化的外设以及它们的初始化顺序。但是,该功能特别麻烦,尤其是当需要初始化的外围设备很多时。main
自动初始化
在计算机上对 C 进行编程以打印hello world
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello world\r\n");
return 1;
}
在这里,我们可以直接用于打印,无需任何初始化步骤;这个想法导致了自动初始化机制。printfRT-Thread
RT-Thread
自动初始化:
int led_init()
{
...
}
INIT_APP_EXPORT(led_init);
int main(int argc, char *argv[])
{
led_on();
rt_kprintf("hello rt thread\r\n");
return 1;
}
自动初始化的中心思想是,在执行函数之前,每个外设的初始化都已完成,所有外设都可以直接在函数中使用。例如,上面的程序直接用于输出并点亮main main rt_kprintfLED
自动初始化的接口
从源代码中截获的自动初始化,如下所示:API RT-Thread
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
机理分析
INIT_EXPORT功能
从初始化函数来看,我们知道它们的最终调用是函数。只有输入的参数不同。我们来看看这个函数的定义:INIT_EXPORT
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
该函数有两个参数;第一个参数指示需要初始化哪个函数,传递函数指针(函数名称),第二个参数指示要将函数指针放入哪个段。接下来让我们进入Macro,在我们跳转到Macro之前,需要了解几个先决条件INIT_EXPORT
- RT_USED
- define RT_USED属性(已使用)
已在目标文件中标记该函数,以防止链接器删除未使用的部分。attribute__(used)
- init_fn_t类型
- typedef int (*init_fn_t)(void);
此处,返回值 定义为具有函数参数的函数指针类型,并重命名intvoidinit_fn_t
##
##
属于C语言,其作用是将两种语言符号组合成一个语言符号
- SECTION
- define SECTION(x) 属性((section(x)))
__attribute__((section(name)))
将功能函数或数据放入指定为的输入段中name
通过上述初步备份,让我们分析以下Macro。将Macro展开为如下所示:INIT_EXPORT
RT_USED const init_fn_t __rt_init_fn SECTION(".rti_fn." level) = fn
这个Macro的功能是将指向函数的指针分配给变量,这个变量类型是,并且存储在指定的段中。因此,在使用自动初始化Macro导出函数后,指向每个初始化函数的指针将存储在这些数据段中。当我们取消引用时,这些指针将在我们执行相应的函数时被采用。fn__rt_init_fnRT_USED const
init_fn_t.rti_fn.level
Segments划分
源代码如下:component.c
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
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");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end, "6.end");
使用Macro导出的上述segments的分布如下表所示:INIT_EXPORT
添加自动初始化后导出的六个segments后,每个segments的分布如下表所示:
rt_components_board_init功能
前往检查函数的实现:rt_components_board_init
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
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)();
}
#endif
}
如果没有,请考虑 ,很明显地发现 正在执行以下内容:RT_DEBUG_INITrt_components_board_init
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)();
}
上面的代码定义了一个指针,当指针的范围在 and 的范围内时,该指针被取消引用,其中指针是自动初始化时放置的函数指针,因此它是函数的执行。也就是说,执行导出的函数。
fn_ptr__rt_init_rti_board_startrt_init_rti_board_endINIT_BOARD_EXPORT(fn)
rt_components_init功能
源代码:
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}
如果没有,请考虑 ,很明显地发现 正在执行以下内容:RT_DEBUG_INITrt_components_init
执行自动初始化函数
启动过程:RT-Thread
Sample
将以下测试代码添加到函数中:main.c
int led_init(void)
{
return 1;
}
INIT_APP_EXPORT(led_init);
编译后的文件如下所示:.map
函数指针位于segments中,函数在执行时会取消引用该指针,即执行函数
__rt_init_led_init.rti_fn.6rt_components_init()led_init
- 分享
- 举报
-
浏览量:606次2024-01-25 16:37:07
-
2024-01-02 18:00:05
-
浏览量:7441次2021-08-11 16:51:39
-
浏览量:3738次2020-12-24 10:37:59
-
2020-08-13 17:36:37
-
浏览量:3788次2020-08-19 16:34:45
-
浏览量:1181次2023-10-08 17:44:55
-
浏览量:2600次2024-01-25 15:00:06
-
浏览量:4367次2021-01-29 10:33:19
-
浏览量:6555次2021-04-15 14:30:22
-
浏览量:852次2024-02-22 15:39:36
-
浏览量:7116次2020-08-14 11:34:19
-
浏览量:9264次2020-12-06 23:24:07
-
浏览量:1386次2024-05-24 18:51:13
-
浏览量:709次2023-09-20 19:02:57
-
浏览量:4947次2020-11-30 09:50:53
-
浏览量:2257次2020-03-18 09:22:40
-
浏览量:4210次2020-10-28 23:08:53
-
浏览量:6505次2020-10-28 23:03:59
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
艾
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明