Bootstrap

RT-Thread INIT_EXPORT宏

INIT_EXPORT宏的作用

#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};

上面这个代码块是这个宏的原型,先说一下这个宏展开之后到底是什么。

这个宏接受两个两个参数,一个是函数指针fn,另一个是字符串level(用于确定初始化函数的等级)。宏展开后,执行const char __rti_##fn##_name[] = #fn; ##fn 是一个预处理器的粘合操作符,用来将 fn 参数与前缀 __rt_init_desc_ 拼接在一起,形成一个新的变量名。也就是说,如果传入的函数指针名字是OLED_Init,那么#fn会将OLED_Init“粘合”、转化为一个字符串,并将这个字符串赋值给__rti_OLED_Init_name这个字符数组,如果你去访问__rti_OLED_Init_name这个数组的话,它的内容是OLED_Init这个字符串。

随后执行RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = { __rti_##fn##_name, fn};这句代码,这句代码比较长。

我们先说一下RT_USED宏和SECTION宏的定义与作用:

  • 最开头的RT_USED宏原型也是在rtdef.h文件中 ,crtl+F一下可以找到这个宏的原型:
    #define RT_USED __attribute__((used))
    这个宏定义的作用是告诉编译器,被标记为 RT_USED 的变量或函数是被使用的,以防止编译器在链接过程中将其优化掉。在编程中,有时会定义一些变量或函数,但在某些情况下可能并没有直接被使用,例如在某些条件下的编译选项不同,或者在某些库中可能有一些函数只在特定情况下被调用。如果没有使用 RT_USED 这样的标记,编译器可能会将这些看似未使用的变量或函数优化掉,从而导致链接错误或者不正确的程序行为。

  • 第二个宏是SECTION,同样也是在rtdef.h文件中被定义,转到定义可以看到,宏定义的原型为:
    #define SECTION(x) __attribute__((section(x)))
    这个宏定义的作用就是告诉编译器将特定变量或函数放置在指定的段(section)中。

    使用__attribute__((section(x)))时,一定要将其放在等号的左边和变量的中间,对于RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = { __rti_##fn##_name, fn}; 来说,也就是要放在__rt_init_desc_##fn这个结构体变量和等号之间。

说完了两个宏,我们先尝试着去掉这两个宏来理解这句代码,也就是这样:
const struct rt_init_desc __rt_init_desc_##fn = { __rti_##fn##_name, fn};

这样来看这句代码是将结构体变量进行了赋值,并将这个结构体标记为const。这个结构体包含了要被初始化函数的一些信息,函数名的字符串,以及初始化函数的函数指针。其实我们在这个结构体变量的命名中也可以看出这个结构体的作用,desc是description的缩写,所以这个结构体是对初始化函数的一些描述。

结合上面所说的RT_USEDSECTION这两个宏的作用,这个结构体被放在了".rti_fn.“level”"的段中,加上RT_USED这个宏进行修饰,告诉编译器这个段中的数据是要被使用的,防止编译器将这个段中的数据被优化掉。

INIT_EXPORT宏的使用

在RTT的API官方文档中,有这么一句话:
在这里插入图片描述

上面那句话中的宏接口就是INIT_EXPORT这个宏,不过RTT又将这个宏抽象了一下
在这里插入图片描述

抽象出了六个层面的初始化接口,分别是BOARD,PREV,DEVICE,COMPONENT,ENV,APP这六个层面,
BAORD层是对硬件的一些初始化,比如USART的初始化
在这里插入图片描述

INIT_PREV_EXPORT这个宏提供了组件预初始化的接口,在注释中可以看到,这个初始化是纯软件层面的(pure software)。

后面的几个接口就是设备、组件、环境、软件层面的初始化了。

这里还有一篇关介绍INIT_EXPORT宏的文章,如果有不太理解的地方大家可以看一下这篇文章:
RT-Thread 的 INIT_BOARD_EXPORT(fn) 宏 实现过程 - 放空飞翔 - 博客园 (cnblogs.com)

;