RT-Thread 自动初始化机制

自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。

在RTT的代码中,有rt_components_init()rt_components_board_init();两个函数,用于实现自动初始化机制。
RTT官方文档的系统启动流程图中,6种注册分别于上述两个函数中实现自动初始化。
在这里插入图片描述

两个初始化函数也很简单,就是调用从__rt_init_rti_board_start__rt_init_rti_board_end的函数执行一遍;__rt_init_rti_board_end__rt_init_rti_end的函数执行一遍。

/**
 * 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)();
    }
}


/**
 * RT-Thread Components Initialization
 */
void rt_components_init(void)
{
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    {
        (*fn_ptr)();
    }
}

typedef int (*init_fn_t)(void);可知,fn_ptr 是一个32bit的指针,刚好stm32是32bit寻址的,可以存储需要初始化的函数。
官方给的注册接口也很简单:

初始化顺序 宏接口 描述
1 INIT_BOARD_EXPORT(fn) 主要是用于纯软件的初始化、没有太多依赖的函数
2 INIT_PREV_EXPORT(fn) 非常早期的初始化,此时调度器还未启动
3 INIT_DEVICE_EXPORT(fn) 外设驱动初始化相关,比如网卡设备
4 INIT_COMPONENT_EXPORT(fn) 组件初始化,比如文件系统或者 LWIP
5 INIT_ENV_EXPORT(fn) 系统环境初始化,比如挂载文件系统
6 INIT_APP_EXPORT(fn) 应用初始化,比如 GUI 应用

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

不同的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")

我们可以简单理解为第二个数字代表的是初始化顺序,可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");

对照着映像文件可以看到:
在这里插入图片描述
选中部分,第一行是一个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,正是我们的函数地址。在这里插入图片描述
最后我们再回到这个初始化的过程(以第一个流程为例):

/**
 * 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)();
    }
}

对照着映像表,从符号名为__rt_init_rti_board_start(注册名为rti_board_start)(段名为0.end)开始的地方,取出其存放的32bit的值作为函数地址,执行这些函数,一直到符号名为__rt_init_rti_board_end(注册名为rti_board_end)(段名为1.end)的地方结束。

发布了323 篇原创文章 · 获赞 63 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/qq_27508477/article/details/100125809
今日推荐