第2章 内核对象与启动流程
各位同学,今天我们来聊聊RT-Thread的“骨架”和“第一口气”。说白了,就是两件事:内核里那些对象是怎么组织的,以及系统上电后是怎么一步步跑起来的。搞懂这个,后面调试起来心里才有底。
2.1 内核对象模型
RT-Thread的内核,本质上是一个面向对象的系统。它把线程、信号量、消息队列、事件、互斥量、定时器、内存池等等,都抽象成了“内核对象”。
为什么要这么干?我刚开始接触的时候也觉得多此一举。直到有一次,我需要遍历系统中所有线程来排查一个死锁问题——嗯,有了统一的对象模型,这件事就变得非常优雅。
2.1.1 对象分类
内核对象分为两大类:
| 类别 | 包含对象 | 特点 |
|---|---|---|
| 静态对象 | 静态线程、静态信号量、静态事件等 | 编译时分配内存,速度快,适合资源受限场景 |
| 动态对象 | 动态线程、动态信号量、动态消息队列等 | 运行时从堆分配,灵活,但需注意内存碎片 |
2.1.2 对象控制块
每个内核对象都有一个控制块,结构大致如下:
struct rt_object
{
char name[RT_NAME_MAX]; // 对象名称
rt_uint8_t type; // 对象类型
rt_uint8_t flag; // 对象标志
rt_list_t list; // 对象链表节点
};
你看,所有对象都挂在一个全局链表上。这意味着什么?意味着你可以通过遍历这个链表,找到任何一个对象。我在项目中就靠这个特性,写了一个“对象查看器”调试命令,直接打印出所有线程的状态和栈使用率。
2.1.3 对象容器
RT-Thread用“对象容器”来管理同一类对象。每个容器对应一种对象类型,比如线程容器、信号量容器。
struct rt_object_information
{
enum rt_object_class_type type; // 对象类型
rt_list_t object_list; // 对象链表头
rt_size_t object_size; // 对象大小
};
2.2 系统启动流程
系统上电后,到底发生了什么?很多同学直接写应用代码,从来没关心过启动过程。但一旦出了问题,比如系统卡在某个地方起不来,你就得顺着这条线去查。
启动流程分三步走:Reset_Handler → entry → rtthread_startup。
2.2.1 Reset_Handler
这是芯片复位后的第一个入口。它做的事情很简单:
- 设置栈指针(SP)
- 调用SystemInit()初始化时钟和硬件
- 跳转到entry
这部分代码通常在startup_stm32xxxx.s汇编文件里。我建议你至少看一遍,不用背,但要知道它干了什么。
2.2.2 entry
entry是C语言环境的入口。它主要做两件事:
- 初始化ZI段(清零)
- 拷贝RW段到RAM
做完这些,C语言就能正常跑了。然后它调用rtthread_startup()。
2.2.3 rtthread_startup
这是RT-Thread内核初始化的核心函数。我把它拆成几个关键步骤:
| 步骤 | 函数/操作 | 说明 |
|---|---|---|
| 1 | rt_hw_interrupt_disable() | 关闭全局中断,防止初始化被打断 |
| 2 | rt_hw_board_init() | 初始化板级硬件(时钟、GPIO、串口等) |
| 3 | rt_system_scheduler_init() | 初始化调度器 |
| 4 | rt_application_init() | 创建主线程(main线程) |
| 5 | rt_system_scheduler_start() | 启动调度器,开始多任务 |
2.3 自动初始化机制
RT-Thread有一个非常实用的特性——自动初始化。你不需要手动调用每个模块的初始化函数,系统会自动帮你按顺序执行。
2.3.1 初始化宏
RT-Thread定义了几个宏,用来标记初始化函数:
INIT_BOARD_EXPORT(fn) // 板级初始化,最早
INIT_PREV_EXPORT(fn) // 纯软件初始化
INIT_DEVICE_EXPORT(fn) // 设备驱动初始化
INIT_COMPONENT_EXPORT(fn) // 组件初始化
INIT_ENV_EXPORT(fn) // 环境初始化
INIT_APP_EXPORT(fn) // 应用初始化,最晚
这些宏会把函数地址放到特定的段里。系统启动时,会按段顺序依次调用。
2.3.2 执行顺序
我画了一张图,帮你理解这个顺序:
2.3.3 使用示例
假设你写了一个LED驱动,想让它自动初始化:
#include <rtthread.h>
static int led_hw_init(void)
{
// 初始化LED的GPIO
rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
rt_kprintf("LED hardware initialized\n");
return 0;
}
INIT_BOARD_EXPORT(led_hw_init);
static int led_app_init(void)
{
// 创建LED控制线程
rt_thread_t tid = rt_thread_create("led",
led_entry,
RT_NULL,
512,
10,
20);
if (tid != RT_NULL)
rt_thread_startup(tid);
return 0;
}
INIT_APP_EXPORT(led_app_init);
2.4 本章小结
这一章我们讲了三个核心内容:
- 内核对象模型——系统里所有资源都是对象,通过统一的方式管理
- 系统启动流程——从Reset_Handler到rtthread_startup,每一步都有明确分工
- 自动初始化机制——用宏标记初始化函数,系统按顺序自动执行
理解这些,你就掌握了RT-Thread的“骨架”。后面讲线程、信号量、消息队列的时候,你会发现它们都是内核对象的一种。启动流程和自动初始化,则是你排查“系统起不来”这类问题的第一把钥匙。