作为一名在嵌入式领域深耕多年的开发者,我经常遇到新手对系统启动流程和C++对象构造时机感到困惑的问题。今天,我将结合RT-Thread实时操作系统,深入剖析从芯片上电到C++对象构造的完整过程。
嵌入式系统的启动过程可以清晰地划分为三个阶段:
这个流程在RT-Thread中的实现非常典型,下面我们重点分析几个关键环节。
不同的编译工具链对启动流程的处理各有特点:
cpp复制// MDK工具链的特殊处理
extern "C" int $Sub$$main(void) {
rt_components_init(); // 初始化所有组件
return $Super$$main(); // 跳转到用户main
}
// IAR工具链的处理
extern "C" int __low_level_init(void) {
return 1; // 允许IAR执行默认初始化
}
// GCC工具链的处理
extern "C" void entry(void) {
// 手动复制.data段
uint32_t *src = &_data_flash_start;
uint32_t *dst = &_data_start;
while(dst < &_data_end) *dst++ = *src++;
// 清零.bss段
uint32_t *bss = &_bss_start;
while(bss < &_bss_end) *bss++ = 0;
rtthread_startup(); // 进入RT-Thread主流程
}
这三种工具链最终都会汇聚到rtthread_startup()函数,开始内核的正式初始化。
让我们深入分析RT-Thread的核心启动函数:
cpp复制int rtthread_startup(void) {
// 第一步:关闭全局中断
rt_base_t level = rt_hw_interrupt_disable();
// 第二步:板级硬件初始化
rt_hw_board_init(); // 包含时钟配置、外设初始化等
// 第三步:显示版本信息
rt_show_version();
// 第四步:初始化系统定时器
rt_system_timer_init();
// 第五步:初始化调度器
rt_system_scheduler_init();
// 第六步:创建系统线程
rt_application_init(); // 主线程
rt_system_timer_thread_init(); // 定时器线程
rt_thread_idle_init(); // 空闲线程
// 第七步:启动调度器(永不返回)
rt_system_scheduler_start();
return 0; // 永远不会执行到这里
}
这个函数的执行顺序经过精心设计,确保各子系统按正确依赖关系初始化。
rt_hw_board_init()函数通常由用户实现,包含以下关键操作:
cpp复制void rt_hw_board_init(void) {
// 1. 配置系统时钟(必须最先完成)
SystemClock_Config(); // 例如配置STM32的PLL
// 2. 初始化SysTick定时器
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
// 3. 初始化堆内存
extern uint8_t _heap_start, _heap_end;
rt_system_heap_init(&_heap_start, &_heap_end);
// 4. 初始化板级组件
rt_components_board_init(); // 注册串口、SPI等驱动
}
这个阶段完成后,系统就具备了基本运行环境:时钟正常、内存管理可用、基础外设就绪。
RT-Thread通过五级初始化机制来管理启动过程,C++全局对象的构造被安排在第三级(组件初始化阶段):
cpp复制void rt_components_init(void) {
// 第一级:预初始化
call_init_functions(1);
// 第二级:设备驱动初始化
call_init_functions(2);
// 第三级:组件初始化(C++构造在这里)
call_init_functions(3); // 包含cplusplus_system_init()
// 第四级:环境初始化
call_init_functions(4);
// 第五级:应用初始化
call_init_functions(5);
}
cplusplus_system_init()函数负责遍历并调用所有全局C++对象的构造函数:
cpp复制int cplusplus_system_init(void) {
extern void (*__ctors_start__)();
extern void (*__ctors_end__)();
for(void (**p)() = &__ctors_start__; p < &__ctors_end__; p++) {
(*p)(); // 调用构造函数
}
return 0;
}
在嵌入式环境中,C++对象的构造顺序至关重要。考虑以下示例:
cpp复制class Logger {
public:
Logger() { rt_kprintf("Logger initialized\n"); }
};
class Sensor {
public:
Sensor() {
// 假设依赖Logger
log.debug("Initializing sensor");
}
};
Logger log; // 全局对象
Sensor temperature; // 另一个全局对象
如果Sensor先于Logger构造,就会导致未定义行为。RT-Thread的初始化机制通过明确的优先级确保了依赖关系的正确性。
C++异常处理依赖于一组ABI函数:
cpp复制// 异常分配
void* __cxa_allocate_exception(size_t size);
// 异常抛出
void __cxa_throw(void* exc, std::type_info* tinfo, void (*dest)(void*));
// 异常捕获
void* __cxa_begin_catch(void* exc);
void __cxa_end_catch();
在ARM平台,异常处理还需要.ARM.exidx段提供栈展开信息,每个函数在此段中占用8字节空间。
对于资源受限的嵌入式系统,异常处理带来的开销需要考虑:
| 组件 | 大小 | 说明 |
|---|---|---|
| .ARM.exidx | 8字节/函数 | 栈展开信息 |
| typeinfo | ~16字节/类 | 类型信息 |
| 异常对象 | 可变 | 包含头部和实际异常数据 |
一个中等规模项目(1000函数,100类)可能增加约10KB的Flash占用。
RT-Thread默认配置为-fno-exceptions,主要考虑:
但在用户态应用(如RT-Thread Smart)中,可以按需开启异常支持。
RT-Thread通过适配层将C++11线程映射到原生线程:
cpp复制class thread {
public:
// 线程构造函数
template<typename F, typename... Args>
explicit thread(F&& f, Args&&... args) {
auto* task = new std::function<void()>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
rt_thread_t t = rt_thread_create(
"cpp_thread",
thread_entry,
task,
4096, // 栈大小
10, // 优先级
20 // 时间片
);
if(t) {
rt_thread_startup(t);
_id._cpp_thread = t;
} else {
delete task;
throw std::system_error(ENOMEM, std::generic_category());
}
}
private:
static void thread_entry(void* arg) {
auto task = static_cast<std::function<void()>*>(arg);
(*task)();
delete task;
}
struct id {
rt_thread_t _cpp_thread;
// 比较操作等...
};
id _id;
};
对于mutex和condition_variable,RT-Thread也提供了对应的适配:
cpp复制class mutex {
public:
mutex() {
rt_mutex_init(&_mutex, "cpp_mutex", RT_IPC_FLAG_FIFO);
}
void lock() {
rt_mutex_take(&_mutex, RT_WAITING_FOREVER);
}
void unlock() {
rt_mutex_release(&_mutex);
}
private:
rt_mutex_t _mutex;
};
这种适配层使得C++标准库能够在RT-Thread上无缝工作,大大提高了代码的可移植性。
通过深入理解这些底层机制,开发者可以更好地驾驭嵌入式C++开发,构建出既高效又可靠的嵌入式系统。