RTT笔记-分析自动初始化机制

首先全局搜索一个任意的自启动宏,便能找到在rtdef.h中由如下定义

#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的定义就就在上方

#ifdef RT_USING_COMPONENTS_INIT 
typedef int (*init_fn_t)(void);
#ifdef _MSC_VER /* we do not support MS VC++ compiler */
    #define INIT_EXPORT(fn, level)
#else
    #if RT_DEBUG_INIT
        struct rt_init_desc
        {
            const char* fn_name;
            const init_fn_t fn;
        };
        #define INIT_EXPORT(fn, level)                                                       \
            const char __rti_##fn##_name[] = #fn;                                            \
            RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \
            { __rti_##fn##_name, fn};
    #else
        #define INIT_EXPORT(fn, level)                                                       \
            RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
    #endif
#endif
#else
#define INIT_EXPORT(fn, level)
#endif

针对上面代码,逐句分析下。
首先RT_USING_COMPONENTS_INIT宏需要在config.h中定义,否则自启动是无效的。
然后使用typedef定义了一个函数指针类型
这里补充一下关于typedef:
目前我知道的typedef有两种用法,其一是起别名,其二是定义新的类型,举个例程说明这两种用法

//生产了新类型fun_p
typedef int (*fun_p)(void);
int app(void)
{
    return 0;
}

typedef struct sTest
{
    fun_p * app_p;
}Test_s; 

Test_s test;
tset.app_p = app;

回到上文,由于#ifdef后的宏均是未定义,所以一路走else,那么就仅仅剩一句话

RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

首先看看RT_USED这个宏,通用定义也在rtdeh.h中

#define RT_USED                     __attribute__((used))
  • attribute((used))标识符作用是使定义被标记的函数或数据即使未使用也不会被编译器优化。
  • init_fn_t是一个函数指针类型
  • __rt_init_##fn是将__rt_init_和我们传入的需要自启动的函数名进行拼接
  • SECTION(".rti_fn."level)也就是 __attribute__((section( ".rti_fn."level ))),

__attribute __((section(“name”)))

该函数便是实现自动初始化的关键了,他的作用是将标记的数据或者函数在编译时放到name的数据段中去。
例如系统中有如下语句

components.c(60) : INIT_EXPORT(rti_start, "0");

在编译后生成的map文件中能够找到对应信息,名叫__rt_init_rti_start 的指针被保存在了.rti_fn.0字段中去

__rt_init_rti_start 0x0801e6b8 Data 4 components.o(.rti_fn.0)

综上那么完整语句的翻译便是: 定义了一个名为(_rt_init+需要自动启的函数名)的函数指针,将其保存在(.rti_fn.level)数据段中,并且及时不使用也不会被编译器优化。

到这里基本就能明白自启动的方式了。也就是逐个建立一个指针指向需要自启动的函数,然后将这些指针保存到特定的数据段中。main启动时候,只需要将数据段中的指针函数全部执行一遍即可。

接下来我们看执行初始化的地方,也就是在components.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");

我们再看看map中的情况

    __rt_init_rti_start                      0x0801e6dc   Data           4  components.o(.rti_fn.0)
    __rt_init_rti_board_start                0x0801e6e0   Data           4  components.o(.rti_fn.0.end)
    __rt_init_rt_hw_spi_init                 0x0801e6e4   Data           4  drv_spi.o(.rti_fn.1)
    __rt_init_rti_board_end                  0x0801e6e8   Data           4  components.o(.rti_fn.1.end)
    __rt_init_ulog_init                      0x0801e6ec   Data           4  ulog.o(.rti_fn.2)
    __rt_init_ulog_console_backend_init      0x0801e6f0   Data           4  console_be.o(.rti_fn.2)
    __rt_init_finsh_system_init              0x0801e6f4   Data           4  shell.o(.rti_fn.6)
    __rt_init_fal_init                       0x0801e6f8   Data           4  fal.o(.rti_fn.6)
    __rt_init_rti_end                        0x0801e6fc   Data           4  components.o(.rti_fn.6.end)
   

我们想自启动的函数__rt_init_rt_hw_spi_init、__rt_init_ulog_init 等都被包夹在了这些标识杆中间。至于他的排序问题,文末将用代码进行测试推论。

按照系统执行顺序来分别看下自启动的两个函数:
首先是 rtthread_startup() →void rt_hw_board_init() →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和__rt_init_rti_board_end便是上面的两个标志杆,是经过宏里面的##拼接后的结果,然后我们再看看上面的map,就发现这个for循环实际上是执行了被包夹的__rt_init_rti_board_start和__rt_init_rt_hw_spi_init,拆解一下就是函数rti_board_start和rt_hw_spi_init。

我们再看第二个自启动的函数
rtthread_startup() →rt_application_init()→void main_thread_entry(void *parameter)→rt_components_init();

    const init_fn_t *fn_ptr;

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

这里和上面类似,只是标志杆变为了level26之间的函数了。也就是level02间的函数是一起执行的,level2~6间的函数是一起执行的。

接下来我们研究一下这个字段的排序问题
首先由已知,在.rti_fn.后面是以数由小到大排序。

image.png

那么尝试一下在后面添加字符,添加两个新的标志杆
image.png

字符排在了数字后面,然后再添加一个大写字母
image.png

A排序到了小写字母之前数字之后,也就是这个排序可能就是ascii码的排序了。

还有个问题就是同字段的两个函数指针的顺序如何呢,例如

    __rt_init_ulog_init                      0x0801e6f8   Data           4  ulog.o(.rti_fn.2)
    __rt_init_ulog_console_backend_init      0x0801e6fc   Data           4  console_be.o(.rti_fn.2)

我将之前的标杆修改为了

//测试用标志杆
static int rti_A(void)
{
   return 0;
}
INIT_EXPORT(rti_A, "2");

//测试用标志杆
static int rti_a(void)
{
   return 0;
}
INIT_EXPORT(rti_a, "2");

static int rti_1(void)
{
   return 0;
}
INIT_EXPORT(rti_1, "2");

然后map结果是:

    __rt_init_rti_start                      0x0801e6e8   Data           4  components.o(.rti_fn.0)
    __rt_init_rti_board_start                0x0801e6ec   Data           4  components.o(.rti_fn.0.end)
    __rt_init_rt_hw_spi_init                 0x0801e6f0   Data           4  drv_spi.o(.rti_fn.1)
    __rt_init_rti_board_end                  0x0801e6f4   Data           4  components.o(.rti_fn.1.end)
    __rt_init_rti_A                          0x0801e6f8   Data           4  components.o(.rti_fn.2)
    __rt_init_rti_a                          0x0801e6fc   Data           4  components.o(.rti_fn.2)
    __rt_init_rti_1                          0x0801e700   Data           4  components.o(.rti_fn.2)
    __rt_init_ulog_init                      0x0801e704   Data           4  ulog.o(.rti_fn.2)
    __rt_init_ulog_console_backend_init      0x0801e708   Data           4  console_be.o(.rti_fn.2)
    __rt_init_finsh_system_init              0x0801e70c   Data           4  shell.o(.rti_fn.6)
    __rt_init_fal_init                       0x0801e710   Data           4  fal.o(.rti_fn.6)
    __rt_init_rti_end                        0x0801e714   Data           4  components.o(.rti_fn.6.end)
  

可以看出排序和我代码顺序有关,也就是应该和编译顺序有关。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容