开源RT线程RTOS自动初始化机制分析

开源RT线程RTOS自动初始化机制分析 2023-03-01 11:22:14 928

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

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区