RTT自动初始化

1. 自动初始化API

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

/* 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")
初始化顺序API描述
1INIT_BOARD_EXPORT(fn)非常早期的初始化,此时调度器还未启动
2INIT_PREV_EXPORT(fn)主要是用于纯软件的初始化、没有太多依赖的函数
3INIT_DEVICE_EXPORT(fn)外设驱动初始化相关,比如网卡设备
4INIT_COMPONENT_EXPORT(fn)组件初始化,比如文件系统或者 LWIP
5INIT_ENV_EXPORT(fn)系统环境初始化,比如挂载文件系统
6INIT_APP_EXPORT(fn)应用初始化,比如 GUI 应用

2. 原理分析

2.1 INIT_EXPORT函数

各个初始化函数可以看出最终调用的都是INIT_EXPORT函数,只是传参不同

#define INIT_EXPORT(fn, level)                                                       \
    RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
/*
	1. fn表示需要初始化的函数,传参为函数指针
	2. level表示将函数指针放到哪一个段
*/

2.2 预备知识

  1. RT_USED

    #define RT_USED                     __attribute__((used))
    /* 标记为attribute__((used))的函数被标记在目标文件中,以避免链接器删除未使用的节。*/
    
  2. init_fn_t 类型

    typedef int (*init_fn_t)(void);
    /* 定义了一个返回值为int,函数参数为void的一个函数指针类型并重命名为init_fn_t。 */
    
  3. SECTION

    #define SECTION(x)                  __attribute__((section(x)))
    /* 将作用的函数或数据放入指定名为name的输入段中 */
    
  4. 将宏展开

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

    函数 fn 的指针赋值给__rt_init_fn这个变量,这个变量的类型是RT_USED const init_fn_t,这个变量存放在指定的段.rti_fn.level中。所以函数使用自动初始化宏导出后,这些数据段中就会存储指向各个初始化函数的指针。当我们对这些指针进行解引用的时候也就相当于执行了相应的函数。

2.3 段的划分

在 component.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");

上面使用 INIT_EXPORT 宏导出的段分布如下表所示

序号段名函数指针/函数名
1.rti_fn.0__rt_init_rti_start
2.rti_fn.0.end__rti_init_rti_board_start
3.rti_fn.1.end__rti_init_rti_board_end
4.rti_fn.6.end__rti_init_rti_end

__rti_init_rti_end

序号段名函数指针/函数名
1.rti_fn.0__rt_init_rti_start
2.rti_fn.0.end__rti_init_rti_board_start
.rti_fn.1INIT_BOARD_EXPORT(fn)
4.rti_fn.1.end__rti_init_rti_board_end
5.rti_fn.2INIT_PREV_EXPORT(fn)
6.rti_fn.3INIT_DEVICE_EXPORT(fn)
7.rti_fn.4INIT_COMPONENT_EXPORT(fn)
8.rti_fn.5INIT_ENV_EXPORT(fn)
9.rti_fn.6INIT_APP_EXPORT(fn)
10.rti_fn.6.end__rti_init_rti_end

2.4 rt_components_board_init 函数

void rt_components_board_init(void)
{
	/* …………………………………… */
    volatile const init_fn_t *fn_ptr;s
    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
}

这段代码定义了一个 fn_ptr 指针,当指针的范围在 __rt_init_rti_board_start 和 __rt_init_rti_board_end 范围之内时就对该指针进行解引用,这里的指针就是自动初始化时放的函数指针,所以这里就相当与函数的执行。也就是执行了 INIT_BOARD_EXPORT(fn) 导出的函数

2.5 rt_components_init 函数

void rt_components_board_init(void)
{
	/* …………………………………… */
	volatile const init_fn_t *fn_ptr;
    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
}

定义了一个 fn_ptr 指针,当指针的范围在 __rt_init_rti_board_end __rt_init_rti_end范围之内时就对该指针进行解引用,这里的指针就是自动初始化时放的函数指针,所以这里就相当与函数的执行。也就是执行了INIT_PREV_EXPORT(fn)INIT_APP_EXPORT(fn) 这两个段之间导出的函数

  • rt_components_board_init()完成了第 1 段,也就是初始化了由INIT_BOARD_EXPORT(fn)的初始化的所有函数,也就是__rt_init_rti_board_start到 __rt_init_rti_board_end之间的函数指针。
  • rt_components_init()完成了第2 到第6 段,也就是按顺序初始化了由INIT_PREV_EXPORT(fn)到INIT_DEVICE_EXPORT(fn)到INIT_COMPONENT_EXPORT(fn)、INIT_ENV_EXPORT(fn)、INIT_APP_EXPORT(fn)初始化的所有函数,也就是从__rt_init_rti_board_end到__rt_init_rti_end之间的函数指针。
  • 当使用自动初始化导出宏去初始化一个函数时,是由系统中的这两个函数进行遍历函数指针执行的。
RTT文件系统初始化时遇到“Corrupted dir pair at {0x0, 0x1}”错误,通常表示文件系统的目录结构出现了损坏。这种问题可能由多种原因引起,包括但不限于存储介质损坏、意外断电、程序异常终止或文件系统未正确卸载等。 以下是一些可能的解决方案: 1. **使用文件系统检查工具** 如果RTT文件系统支持类似`fsck`的检查和修复工具,可以在系统启动前运行该工具来修复文件系统中的错误。这通常需要将存储介质挂载为只读模式,以避免进一步的文件系统损坏[^1]。 2. **手动检查存储介质** 如果无法使用自动工具修复,可以尝试手动检查存储介质的内容。通过读取存储介质的原始数据,查看目录结构是否确实损坏,并尝试手动恢复关键的目录信息。 3. **从备份恢复** 如果文件系统损坏严重且无法修复,可以从备份中恢复文件系统。在嵌入式系统中,通常可以使用预定义的文件系统镜像进行恢复。 4. **使用NFS进行远程调试** 如果调试过程中频繁出现文件系统损坏问题,可以考虑使用NFS根文件系统进行测试。这样可以避免本地文件系统的损坏,并且便于调试和恢复。 5. **增加日志功能以定位问题** 在驱动程序或文件系统初始化代码中添加日志输出,记录关键的初始化步骤和状态。通过修改`console_loglevel`变量,将日志输出到控制台,有助于定位文件系统损坏发生的具体位置[^1]。 6. **检查文件系统实现** 如果使用的是自定义文件系统或第三方实现,检查其代码逻辑是否正确处理了目录结构的创建和维护。确保在文件系统初始化过程中,目录对的读取和写入操作是原子的,并且在发生异常时能够正确回滚。 7. **验证存储介质的可靠性** 存储介质的物理损坏也可能导致文件系统初始化失败。使用存储介质的诊断工具检查其健康状态,并确保其读写操作正常。 ### 示例:修改console_loglevel以输出调试信息 ```c #include <linux/kernel.h> #include <linux/console.h> // 修改控制台日志级别以输出调试信息 console_loglevel = 8; // 8 表示输出所有级别的日志 printk(KERN_DEBUG "File system initialization started\n"); ``` ### 相关问题 1. 如何在Linux系统中使用fsck工具检查和修复文件系统错误? 2. 在嵌入式系统中,如何创建和恢复文件系统镜像? 3. 如何配置NFS根文件系统以便于调试? 4. 文件系统中的目录对损坏可能由哪些原因引起? 5. 如何在驱动程序中添加日志输出以帮助调试?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值