RT-thread提供了组件化功能,具体实现是在components/init文件夹下components.c文件中实现的。应用组件化功能首先在rtconfig.h中添加宏定义#define RT_USING_COMPONENTS_INIT;若需要启用调试模式,则还要添加#define RT_DEBUG_INIT 1。

void rt_components_board_init(void)
{
#ifndef _MSC_VER
#if RT_DEBUG_INIT //启用初始化调试模式,主要目的为将各个组件初始化的状态通过串口打印到PC端(在rtconfig.h中宏定义为1)
int result;
const struct rt_init_desc *desc; //rt_init_desc是在rtdef.h中定义的结构体类型
for (desc = &__rt_init_desc_rti_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}

#endif
#endif
}
void rt_components_init(void)
{
#ifndef _MSC_VER
#if RT_DEBUG_INIT //启用初始化调试模式
int result;
const struct rt_init_desc *desc; //rt_init_desc为在rtdef.h中定义的结构体类型,这里定义了指向该结构体类型的指针变量 rt_kprintf("do components intialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++) //注意这里的 & 符号
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
const init_fn_t *fn_ptr; //定义指向该函数类型的指针,函数指针变量fn_ptr指向的是初始化函数首地址,fn_ptr本身并不代表初始化函数首地址
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++) //注意这里的 & 符号
{
(*fn_ptr)(); //从函数指针中取出初始化函数首地址(函数名)并进行初始化
}

#endif
#else
#ifdef RT_USING_MODULE
rt_system_module_init();
#endif #ifdef RT_USING_FINSH
/* initialize finsh */
finsh_system_init();
finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif #ifdef RT_USING_LWIP
/* initialize lwip stack */
/* register ethernetif device */
eth_system_device_init(); /* initialize lwip system */
lwip_system_init();
rt_kprintf("TCP/IP initialized!\n");
#endif #ifdef RT_USING_DFS
/* initialize the device file system */
dfs_init(); #ifdef RT_USING_DFS_ELMFAT
/* initialize the elm chan FatFS file system*/
elm_init();
#endif #if defined(RT_USING_DFS_NFS) && defined(RT_USING_LWIP)
/* initialize NFSv3 client file system */
nfs_init();
#endif #ifdef RT_USING_DFS_YAFFS2
dfs_yaffs2_init();
#endif #ifdef RT_USING_DFS_UFFS
dfs_uffs_init();
#endif #ifdef RT_USING_DFS_JFFS2
dfs_jffs2_init();
#endif #ifdef RT_USING_DFS_ROMFS
dfs_romfs_init();
#endif #ifdef RT_USING_DFS_RAMFS
dfs_ramfs_init();
#endif #ifdef RT_USING_DFS_DEVFS
devfs_init();
#endif
#endif /* end of RT_USING_DFS */ #ifdef RT_USING_NEWLIB
libc_system_init(RT_CONSOLE_DEVICE_NAME);
#else
/* the pthread system initialization will be initiallized in libc */
#ifdef RT_USING_PTHREADS
pthread_system_init();
#endif
#endif #ifdef RT_USING_RTGUI
rtgui_system_server_init();
#endif #ifdef RT_USING_USB_HOST
rt_usb_host_init();
#endif
#endif
}

上面代码红色粗体是组件初始化的入口,是一个函数指针。init_fn_t 的定义在rtdef.h中,如下所示:

/* initialization export */
#ifdef RT_USING_COMPONENTS_INIT //在rtconfig.h中进行宏定义,则启用RT-thread的组件初始化功能
typedef int (*init_fn_t)(void); //对指向int ()(void)函数类型的指针类型取别名init_fn_t。 利用这个别名可定义指向该函数类型的指针,也可用于直接定义该类型的函数名
#ifdef _MSC_VER /* we do not support MS VC++ compiler *///这里没有采用microsoft VC++ complier
#define INIT_EXPORT(fn, level)
#else
#if RT_DEBUG_INIT //启用初始化调试模式
struct rt_init_desc
{
const char* fn_name;
const init_fn_t fn; //这里定义的fn并不是函数指针,而是直接定义函数名
};
#define INIT_EXPORT(fn, level) \
const char __rti_##fn##_name[] = #fn; \ //C语言中#连接符是把传递过来的参数当成字符串进行替代
const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \ //定义结构体变量__rt_init_desc_##fn
{ __rti_##fn##_name, fn};
#else
#define INIT_EXPORT(fn, level) \
const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn //这里定义的__rt_init_##fn并不是函数指针,而是直接定义函数名

#endif
#endif
#else
#define INIT_EXPORT(fn, level)
#endif /* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* device/component/fs/app init routines will be called in init_thread */
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "2")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "3")
/* file system initialization (dfs-elm, dfs-rom, ...) */
#define INIT_FS_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")

其中typdef int (*init_fn_t)(void)的意思是定义init_fn_t为指向函数的指针类型,该函数返回int类型值。这样一来,我们对(*init_fn_t)()的意思就清楚了。

INIT_EXPOT(fn,level) 的表达式是const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn,其中##连词符。

## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#连接符是把传递过来的参数当成字符串进行替代。

SECTION的定义为:

      #define SECTION(x)   __attribute__((section(x)))

RealView 编译工具 编译器参考指南中给出了下面的解释:

   __attribute__((section("name")))

通常,ARM 编译器将它生成的对象放在节中,如 data 和 bss。但是,您可能需要使用其他数据节,或者希望变量出现在特殊节中,例如,便于映射到特殊硬件。section 属性指定变量必须放在特定数据节中。如果使用 section 属性,则将只读变量放在 RO 数据节中,而将读写变量放在 RW 数据节中,除非您使用 zero_init 属性。在这种情况下,变量被放在 ZI 节中。到此,意思已经很明了了,编译器可以根据对section("name")中的name指定,可以将它生成的数据放到特定的数据节中。

类似的这样的方式,Linux也提供了一些借鉴,把一个函数的地址(注意是函数地址,而不是函数本身)输出到一个独立的section中,同时按照一定顺序进行排列,例如:
.rti_fn.0
.rti_fn.1
.rti_fn.2
...
.rti_fn.7
这样几个section(这样几个不同的section也给出了排列的顺序)。同时把.rti_fn.0和.rti_fn.7保留给系统使用,分别定义出两个桩放置在这两个点上。

也可以按照RT-Thread的形式定义简化的宏:
typedef int (*init_fn_t)(void);
#define INIT_EXPORT(fn, level)        \
        const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

#define INIT_BOARD_EXPORT(fn)                INIT_EXPORT(fn, "1")
#define INIT_CPU_EXPORT(fn)                     INIT_EXPORT(fn, "2")
#define INIT_DEVICE_EXPORT(fn)                INIT_EXPORT(fn, "3")
#define INIT_COMPONENT_EXPORT(fn)         INIT_EXPORT(fn, "4")
#define INIT_FS_EXPORT(fn)                       INIT_EXPORT(fn, "5")
#define INIT_APP_EXPORT(fn)                     INIT_EXPORT(fn, "6")
INIT_EXPORT宏用于输出一个函数到初始化序列中,相应的可以定义一些更简化的宏。
这样两个桩可以定义成:
static int rti_start(void)
{
        return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_end(void)
{
        return 0;
}
INIT_EXPORT(rti_end,"7");
根据这两个桩的位置,简化的rt_components_init()函数就可以变成:
void rt_components_init(void)
{
        const init_fn_t* fn_ptr;
        for (fn_ptr = &__rt_init_rti_start; fn_ptr < &__rt_init_rti_end; )
        {
                (*fn_ptr)();
                fn_ptr ++;
        }
}

事实上,aozima做了工程测试得到了验证。工程编译后,从map文件找到相关部分内容:

InitFuncSym$$Base                   0x00000e18   Number    0init_1.o(InitFuncSym)

__rt_init_init_1                         0x00000e18   Data         4init_1.o(InitFuncSym)

__rt_init_init_2                         0x00000e1c   Data         4init_2.o(InitFuncSym)

__rt_init_init_3                         0x00000e20   Data         4init_3.o(InitFuncSym)

__rt_init_init_4                         0x00000e24   Data         4init_4.o(InitFuncSym)

__rt_init_init_5                         0x00000e28   Data         4init_5.o(InitFuncSym)

__rt_init_init_6                         0x00000e2c   Data         4init_6.o(InitFuncSym)

InitFuncSym$$Limit                  0x00000e30   Number    0init_6.o(InitFuncSym)

尽管数据存放在不同的文件,但从这里这可以看到是空间连续分配的。

总之,通过定义

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

可以系统各部分的组件通过INIT_EXPORT(fn,level)放到一个特定代码段当中,简言之,当我们要初始化某个组件时,定义完这个初始化函数后,根据上面宏定义的注释,在其下面接着放一条INIT_XXX_EXPORT(fn)就可以了。相当于一个指定到特定代码段的隐形调用,而且要清楚这个段中是不同组件初始化函数的入口地址,例如:

int my_init_fun(void) {... ...}

INIT_XXX_EXPORT(my_init_fun)

例如在finsh组件shell.c中:

int finsh_system_init(void) {......}

INIT_COMPONENT_EXPORT(finsh_system_init);

注意:定义的初始化函数必须满足输入参数类型为void,返回类型为int,即为typedef int (*init_fn_t)(void)中定义的函数类型。

RT-thread组件初始化代码分析的更多相关文章

  1. 【DWM1000】 code 解密2一 工程初始化代码分析

    instance_init 函数追下去,绝大多数的代码都在初始化如下结构体 typedef struct { INST_MODE mode; instance_init -ANCHOR //insta ...

  2. openstack学习之neutron ml2初始化代码分析

    这里没有 去详细考虑neutron server怎么初始化的,而是直接从加载插件的地方开始分析.首先我们看下下面这个文件. Neutron/api/v2/router.py class APIRout ...

  3. Linux时间子系统之(十七):ARM generic timer驱动代码分析

    专题文档汇总目录 Notes:ARM平台Clock/Timer架构:System counter.Timer以及两者之间关系:Per cpu timer通过CP15访问,System counter通 ...

  4. Linux kernel的中断子系统之(七):GIC代码分析

    返回目录:<ARM-Linux中断系统>. 总结: 原文地址:<linux kernel的中断子系统之(七):GIC代码分析> 参考代码:http://elixir.free- ...

  5. Linux中断 - GIC代码分析

    一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V ...

  6. Linux时间子系统(十七) ARM generic timer驱动代码分析

    一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单 ...

  7. JS日期级联组件代码分析及demo

    最近研究下JS日期级联效果 感觉还不错,然后看了下kissy也正好有这么一个组件,也看了下源码,写的还不错,通过google最早是在2011年 淘宝的虎牙(花名)用原审JS写了一个(貌似据说是从YUI ...

  8. RT Thread的SPI设备驱动框架的使用以及内部机制分析

    注释:这是19年初的博客,写得很一般,理解不到位也不全面.19年末得空时又重新看了RTThread的SPI和GPIO,这次理解得比较深刻.有时间时再整理上传. -------------------- ...

  9. RT thread 设备驱动组件之USART设备

    本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...

随机推荐

  1. python 的 购物小程序

    money = input('请输入您的工资:') shop = [("iphone",5800),("ipod",3000),("book" ...

  2. Sublime Text 3 新手上路:必要的安裝、設定與基本使用教學

    http://blog.miniasp.com/post/2014/01/07/Useful-tool-Sublime-Text-3-Quick-Start.aspx

  3. Zookeeper原理和实战开发经典视频教程 百度云网盘下载

    Zookeeper原理和实战开发 经典视频教程 百度云网盘下载 资源下载地址:http://pan.baidu.com/s/1o7ZjPeM   密码:r5yf   

  4. Caliburn.Micro 杰的入门教程5,Window Manager 窗口管理器

    Caliburn.Micro 杰的入门教程1(翻译)Caliburn.Micro 杰的入门教程2 ,了解Data Binding 和 Events(翻译)Caliburn.Micro 杰的入门教程3, ...

  5. 抓猫_KEY

    抓猫 题面如下: [ 题目描述] 流浪猫布满城市的每一个角落, 非常影响市容市貌, 作为城市聘请的抓猫者, 你有一 种捕捉器, 一定可以捕捉到所有走到里面的猫, 更加幸运的是你有一个非常厉害的动物心理 ...

  6. 北京Uber优步司机奖励政策(12月9日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. 请求头(request)和响应头(response)

    说一说常见的请求头和相应头都有什么呢? 1)请求(客户端->服务端[request]) GET(请求的方式) /newcoder/hello.html(请求的目标资源) HTTP/1.1(请求采 ...

  8. 「日常训练」 Genghis Khan the Conqueror(HDU-4126)

    题意 给定\(n\)个点和\(m\)条无向边(\(n\le 3000\)),需要将这\(n\)个点连通.但是有\(Q\)次(\(Q\le 10^4\))等概率的破坏,每次破坏会把\(m\)条边中的某条 ...

  9. 说一说VIN码识别,车架号识别那些事

    对于有车一族的朋友来说,日常接触比较多的是车牌.行驶证.驾驶证,而知道VIN码/车架号码的比较少. 其实,对于车辆来说,VIN码/车架号码非常重要,它就像人的身份证一样,VIN码/车架号码是车辆唯一的 ...

  10. 加油吧 骚年QAQ

    本随笔文章,由个人博客(鸟不拉屎)转移至博客园 写于:2017 年 11 月 08 日 原地址:https://niaobulashi.com/archives/fighting.html --- 想 ...