输入段、输出段是相对link过程来说的。我们写的.C文件编译后生成目标文件.o,这些.o文件由一段一段代码组成,对于link来说,这些是输入段。link过程把这些段重新编排顺序,成生elf或binary文件烧写到flash中,这两种文件也是由段组成,这些段为输出段。
__attribute__的section属性只指定对象的输入段,它并不能影响所指定对象最终会放在可执行文件的什么段。
例子:
定义一个变量var=0,“attribute((section(".xdata")))” 是将变量var放入名为.xdata的输入段。
(注意:__attribute__这种用法中的括号好像很严格,这里的几个括号好象一个也不能少。)
int var __attribute__((section(".xdata"))) = 0;
定义一个函数int functionA(void),并放入名叫.xinit的输入段。
static int __attribute__((section(".xinit"))) functionA(void)
{
.....
}
attribute((section(".xinit"))) 的作用:
1、定义了段名xinit
2、并把被标识的变量或函数存放入段xinit内
3、如果有2个以上的变量,存放到同一段内,按变量在c文件中的顺序排列
4、如果几个段 段名相近,如.xinit0,.xinit1,.xinit2…则编译时会按命名规则紧密排列在一起,这一点很重要。
注意section 是小写。
简化成:
typedef int (*init_fn_t)(void);
定义init_fn_t为无输入参数,返回值为int的函数指针
在rtdef.h中定义了#define SECTION(x) __attribute__((section(x)))
把标识x放到x段内
则下面这个宏
#define INIT_EXPORT(fn, level) RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
即是:
RT_USED const init_fn_t __rt_init_##fn = fn
定义一个函数形指针__rt_init_##fn,并把fn的值赋给它。
SECTION(".rti_fn." level)括号内level是输入的参数,前面号引号内的表示字符串,即.rti_fn.level
至于宏RT_USED,通用定义也在rtdeh.h中,它的作用与上面不同,它是防止优化
#define RT_USED __attribute__((used)) //作用是使 被标记的函数 或 变量,即使不使用也不会被优化掉
_rt_init##fn 是将__rt_init_和传入的函数名进行拼接
综上,意思就是定义了一个名为(_rt_init+函数名)的函数指针,同时把函数fn的地址赋给该指针,该指针被保存在(.rti_fn.level)段中
例如:INIT_EXPORT(rti_start, “0”);
RT_USED const init_fn_t __rt_init_rti_start attribute((section( .rti_fn.0 ))) = rti_start
意思就是定义了一个名为 __rt_init_rti_start 函数指针,同时把函数rti_start的地址赋给该指针,将该指针保存在.rti_fn.0 数据段中
以下分析是根据工程 rtthread_simulator_v0.1.0
上面的四个空函数,通过调用宏定义了4个段名,每个段名同时也有一个指针名与它对应
其它地方同时还定义了三个段名,如下;
在rtdef.h中 定义#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, “6”)
综上,总共就有7个段名。编译后,查看.map文件,这7个段名是按名字顺序排列在一起。
如下图,右则6个蓝色的函数代表了6级初始化等级。其中第一级板给初始化,通过rt_components_board_init()完成。另外5级需要等到系统启动后通过rt_components_init() 函数完成。
下面的函数,执行初始化
参考以下三遍文章:
https://www.jianshu.com/p/9d377ddc8acc
https://blog.csdn.net/jxgxlm2008/article/details/51820854
https://blog.csdn.net/yang1111111112/article/details/93982354