RT-Thread 自动初始化机制

[复制链接]
1217|10
 楼主| keer_zu 发表于 2022-2-10 16:11 | 显示全部楼层 |阅读模式
自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。

例如rt-thread: 411746204c861a4469.png

364326204c8a007a60.png
 楼主| keer_zu 发表于 2022-2-10 16:14 | 显示全部楼层
在RTT的代码中,有rt_components_init()rt_components_board_init();两个函数,用于实现自动初始化机制。
RTT官方文档的系统启动流程图中,6种注册分别于上述两个函数中实现自动初始化。


463966204c9698dd73.png


 楼主| 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的函数执行一遍。


  1. /**
  2. * [url=home.php?mod=space&uid=247401]@brief[/url]  Onboard components initialization. In this function, the board-level
  3. *         initialization function will be called to complete the initialization
  4. *         of the on-board peripherals.
  5. */
  6. void rt_components_board_init(void)
  7. {
  8. #if RT_DEBUG_INIT
  9.     int result;
  10.     const struct rt_init_desc *desc;
  11.     for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
  12.     {
  13.         rt_kprintf("initialize %s", desc->fn_name);
  14.         result = desc->fn();
  15.         rt_kprintf(":%d done\n", result);
  16.     }
  17. #else
  18.     volatile const init_fn_t *fn_ptr;

  19.     for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
  20.     {
  21.         (*fn_ptr)();
  22.     }
  23. #endif /* RT_DEBUG_INIT */
  24. }

  25. /**
  26. * @brief  RT-Thread Components Initialization.
  27. */
  28. void rt_components_init(void)
  29. {
  30. #if RT_DEBUG_INIT
  31.     int result;
  32.     const struct rt_init_desc *desc;

  33.     rt_kprintf("do components initialization.\n");
  34.     for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
  35.     {
  36.         rt_kprintf("initialize %s", desc->fn_name);
  37.         result = desc->fn();
  38.         rt_kprintf(":%d done\n", result);
  39.     }
  40. #else
  41.     volatile const init_fn_t *fn_ptr;

  42.     for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
  43.     {
  44.         (*fn_ptr)();
  45.     }
  46. #endif /* RT_DEBUG_INIT */
  47. }
  48. #endif /* RT_USING_COMPONENTS_INIT */


 楼主| keer_zu 发表于 2022-2-10 16:31 | 显示全部楼层
typedef int (*init_fn_t)(void);可知,fn_ptr 是一个32bit的指针,刚好stm32是32bit寻址的,可以存储需要初始化的函数。
官方给的注册接口也很简单:
814906204cd3299ce9.png

我们只需要根据自己的需求,进行相应的函数注册,就可以实现自动初始化了,但具体这个注册过程是怎么实现的呢?可以查看这些INIT的定义。
 楼主| keer_zu 发表于 2022-2-10 16:32 | 显示全部楼层
不同的INIT对应了同一个函数:INIT_EXPORT,这个函数有两个参数,第一个是传入的函数指针,第二个是一个数字。


  1. /* board init routines will be called in board_init() function */
  2. #define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")

  3. /* pre/device/component/env/app init routines will be called in init_thread */
  4. /* components pre-initialization (pure software initilization) */
  5. #define INIT_PREV_EXPORT(fn)            INIT_EXPORT(fn, "2")
  6. /* device initialization */
  7. #define INIT_DEVICE_EXPORT(fn)          INIT_EXPORT(fn, "3")
  8. /* components initialization (dfs, lwip, ...) */
  9. #define INIT_COMPONENT_EXPORT(fn)       INIT_EXPORT(fn, "4")
  10. /* environment initialization (mount disk, ...) */
  11. #define INIT_ENV_EXPORT(fn)             INIT_EXPORT(fn, "5")
  12. /* appliation initialization (rtgui application etc ...) */
  13. #define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")


 楼主| keer_zu 发表于 2022-2-10 16:34 | 显示全部楼层
我们可以简单理解为第二个数字代表的是初始化顺序,可INIT_EXPORT具体是怎么实现的呢?来看下它的定义:


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



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


  1.   #define RT_USED                     __attribute__((used))
  2.   typedef int (*init_fn_t)(void);
  3.   #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:
  1. /*
  2. * Components Initialization will initialize some driver and components as following
  3. * order:
  4. * rti_start         --> 0
  5. * BOARD_EXPORT      --> 1
  6. * rti_board_end     --> 1.end
  7. *
  8. * DEVICE_EXPORT     --> 2
  9. * COMPONENT_EXPORT  --> 3
  10. * FS_EXPORT         --> 4
  11. * ENV_EXPORT        --> 5
  12. * APP_EXPORT        --> 6
  13. *
  14. * rti_end           --> 6.end
  15. *
  16. * These automatically initialization, the driver or component initial function must
  17. * be defined with:
  18. * INIT_BOARD_EXPORT(fn);
  19. * INIT_DEVICE_EXPORT(fn);
  20. * ...
  21. * INIT_APP_EXPORT(fn);
  22. * etc.
  23. */
  24. static int rti_start(void)
  25. {
  26.     return 0;
  27. }
  28. INIT_EXPORT(rti_start, "0");

  29. static int rti_board_start(void)
  30. {
  31.     return 0;
  32. }
  33. INIT_EXPORT(rti_board_start, "0.end");

  34. static int rti_board_end(void)
  35. {
  36.     return 0;
  37. }
  38. INIT_EXPORT(rti_board_end, "1.end");

  39. static int rti_end(void)
  40. {
  41.     return 0;
  42. }
  43. INIT_EXPORT(rti_end, "6.end");



 楼主| keer_zu 发表于 2022-2-10 16:40 | 显示全部楼层
对照着映像文件可以看到:

381306204ced6e70c8.png

选中部分,第一行是一个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中找到一行:


  1. INIT_APP_EXPORT(finsh_system_init);


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


239666204cf30c9c2a.png

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


772316204cf68be117.png


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


578206204cf86b9542.png
 楼主| keer_zu 发表于 2022-2-10 16:41 | 显示全部楼层
最后我们再回到这个初始化的过程(以第一个流程为例):
  1. /**
  2. * RT-Thread Components Initialization for board
  3. */
  4. void rt_components_board_init(void)
  5. {
  6.     const init_fn_t *fn_ptr;

  7.     for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
  8.     {
  9.         (*fn_ptr)();
  10.     }
  11. }
 楼主| 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)的地方结束。
 楼主| keer_zu 发表于 2022-2-10 17:44 | 显示全部楼层
 楼主| keer_zu 发表于 2022-2-10 17:44 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

1488

主题

12949

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部