打印

RT-Thread 自动初始化机制

[复制链接]
819|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2022-2-10 16:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。

例如rt-thread:


使用特权

评论回复

相关帖子

沙发
keer_zu|  楼主 | 2022-2-10 16:14 | 只看该作者
在RTT的代码中,有rt_components_init()rt_components_board_init();两个函数,用于实现自动初始化机制。
RTT官方文档的系统启动流程图中,6种注册分别于上述两个函数中实现自动初始化。





使用特权

评论回复
板凳
keer_zu|  楼主 | 2022-2-10 16:17 | 只看该作者
两个初始化函数也很简单,就是调用从__rt_init_rti_board_start__rt_init_rti_board_end的函数执行一遍;__rt_init_rti_board_end__rt_init_rti_end的函数执行一遍。


/**
* [url=home.php?mod=space&uid=247401]@brief[/url]  Onboard components initialization. In this function, the board-level
*         initialization function will be called to complete the initialization
*         of the on-board peripherals.
*/
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_INIT */
}

/**
* @brief  RT-Thread Components Initialization.
*/
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_INIT */
}
#endif /* RT_USING_COMPONENTS_INIT */


使用特权

评论回复
地板
keer_zu|  楼主 | 2022-2-10 16:31 | 只看该作者
typedef int (*init_fn_t)(void);可知,fn_ptr 是一个32bit的指针,刚好stm32是32bit寻址的,可以存储需要初始化的函数。
官方给的注册接口也很简单:


我们只需要根据自己的需求,进行相应的函数注册,就可以实现自动初始化了,但具体这个注册过程是怎么实现的呢?可以查看这些INIT的定义。

使用特权

评论回复
5
keer_zu|  楼主 | 2022-2-10 16:32 | 只看该作者
不同的INIT对应了同一个函数:INIT_EXPORT,这个函数有两个参数,第一个是传入的函数指针,第二个是一个数字。


/* 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")


使用特权

评论回复
6
keer_zu|  楼主 | 2022-2-10 16:34 | 只看该作者
我们可以简单理解为第二个数字代表的是初始化顺序,可INIT_EXPORT具体是怎么实现的呢?来看下它的定义:


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



emm……去查下这些宏的定义:


  #define RT_USED                     __attribute__((used))
  typedef int (*init_fn_t)(void);
  #define SECTION(x)                  __attribute__((section(x)))



由GCC手册可知: __attribute__((used))表示这个标记这个东西是使用过的,避免出现如: warning: #177-D: variable "a" was declared but never referenced的警告。
__attribute_unused__和__attribute_used__的作用
在GCC的宏中,##后面跟变量名。
__attribute__((section(x)))则表示fn被放置于指定段中。
可以找到在RTT的初始化过程中,有如下各种INIT:
/*
* Components Initialization will initialize some driver and components as following
* order:
* rti_start         --> 0
* BOARD_EXPORT      --> 1
* rti_board_end     --> 1.end
*
* DEVICE_EXPORT     --> 2
* COMPONENT_EXPORT  --> 3
* FS_EXPORT         --> 4
* ENV_EXPORT        --> 5
* APP_EXPORT        --> 6
*
* rti_end           --> 6.end
*
* These automatically initialization, the driver or component initial function must
* be defined with:
* INIT_BOARD_EXPORT(fn);
* INIT_DEVICE_EXPORT(fn);
* ...
* INIT_APP_EXPORT(fn);
* etc.
*/
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");



使用特权

评论回复
7
keer_zu|  楼主 | 2022-2-10 16:40 | 只看该作者
对照着映像文件可以看到:



选中部分,第一行是一个Section,叫做.rti_fn.0,这个内容实际是我们通过INIT_EXPORT(rti_start, "0");完成的,我们把函数rti_start改名为__rt_init_rti_start,存入.rti_fn.0这个地方。
同样的,INIT_EXPORT(rti_board_start, "0.end");、INIT_EXPORT(rti_board_end, "1.end");、INIT_EXPORT(rti_end, "6.end");也是这里插入的。
注意在这个映像文件中的第2728行,多了一个内容,在shell.c中找到一行:


INIT_APP_EXPORT(finsh_system_init);


通过上面我们知道,通过APP_EXPORT导入的会存放在名为6的Section中,所以这里应该是存放的finsh_system_init的函数指针。
通过Debug查看0x080088e4中的内容,其值为0x08003D39。




监视函数finsh_system_init,其值正好是0x08003D39。





因为其是Thumb指令,所以监视到的最后一个bit为1,正是我们的函数地址。



使用特权

评论回复
8
keer_zu|  楼主 | 2022-2-10 16:41 | 只看该作者
最后我们再回到这个初始化的过程(以第一个流程为例):
/**
* RT-Thread Components Initialization for board
*/
void rt_components_board_init(void)
{
    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)();
    }
}

使用特权

评论回复
9
keer_zu|  楼主 | 2022-2-10 16:41 | 只看该作者
对照着映像表,从符号名为__rt_init_rti_board_start(注册名为rti_board_start)(段名为0.end)开始的地方,取出其存放的32bit的值作为函数地址,执行这些函数,一直到符号名为__rt_init_rti_board_end(注册名为rti_board_end)(段名为1.end)的地方结束。

使用特权

评论回复
10
keer_zu|  楼主 | 2022-2-10 17:44 | 只看该作者
11
keer_zu|  楼主 | 2022-2-10 17:44 | 只看该作者
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1313

主题

12264

帖子

53

粉丝