系统启动与运行框架
AC792N 系列 SDK 的系统启动与运行框架负责从芯片复位到应用层 app_main() 执行的完整引导链,包括双核(CPU0/CPU1)启动、分级 initcall 初始化机制、系统定时器与性能计数器、应用任务与消息循环,以及 C++ 运行时支撑。它是所有 AC792N 应用(如 wifi_bbm、wifi_camera、wifi_soundbox)共用的基础运行环境。
Purpose and Scope
本页面深入解析 SDK 中所有应用共用的启动与运行时基础设施:
- 启动入口与双核引导流程(
cpu1_main、app_task_handler) - 分级 initcall 机制(
early_initcall/platform_initcall/initcall/module_initcall/late_initcall) - 应用任务与消息循环模型(
os_task_pend("taskq", ...)) - 运行时支撑设施:
errno/jiffies/sysconf、C++ ABI 桩(__cxa_*)、标准库桩函数 - 调试与保护机制:RTOS 栈检查、内存泄漏检测、dlog 日志、norflash 关键区域擦写保护
以下主题属于其他页面,不在本页展开:具体外设驱动初始化(asm/includes.h 中的平台外设)、各应用自身的业务逻辑(app_main 的具体实现)、RTOS 内核调度细节。
Overview
在 AC792N SDK 中,系统启动被设计为**"极简引导 + 分级初始化 + 独立应用任务"**三层结构:
- 极简引导:
cpu1_main()仅完成中断、调试与 RTOS 内核启动,保证系统以最小代价进入多任务环境; - 分级初始化:
app_task_handler()在一个专用任务中按依赖顺序执行 5 级 initcall,从板级早期初始化(board_early_init)逐步推进到平台初始化、模块初始化,最后是应用核心初始化(app_core_init)与app_main(); - 独立运行:
app_main()返回后,任务进入os_task_pend("taskq", msg, ...)消息循环,由事件驱动应用运行。
这种设计意图(WHY):外设与模块之间存在严格依赖关系(例如 norflash 关键区域保护必须等 VM 初始化完成后再注册),分层 initcall 让模块以"声明式"方式注册自己的初始化函数,由框架保证顺序,避免在每个应用中手工维护初始化清单。同时,将应用主逻辑放在独立任务中,使其不阻塞系统级初始化,并天然获得消息队列的并发安全模型。
关键概念:
| 概念 | 说明 |
|---|---|
initcall | 通过链接段(section)收集的初始化函数注册机制,按级别分组执行 |
app_task_handler | 系统启动任务入口,串行执行全部初始化并承载应用主循环 |
taskq | 应用任务挂起的消息队列,驱动事件处理 |
perf_counter | 性能计数器,提供 delay_us/delay_ms/timer_get_ms 等时间接口 |
cpu1_run_flag | 标记 CPU1 是否完成启动,供 CPU0 同步使用 |
Architecture
flowchart TD
subgraph sg_CPU0["CPU0 主核"]
MainEntry["main() / 芯片复位入口"]
MainEntry -->|"唤醒 CPU1"| CPUSync["cpu1_run_flag 同步"]
end
subgraph sg_CPU1["CPU1 从核(app 运行核)"]
CPU1Main["cpu1_main()"]
CPU1Main -->|"开启分支预测"| IRQInit["interrupt_init()"]
IRQInit --> DebugInit["debug_init()"]
DebugInit -->|"CPU_CORE_NUM > 1"| OSStart["os_start() RTOS 内核启动"]
OSStart --> AppTask["app_task_handler() 系统启动任务"]
end
subgraph sg_Init["分级初始化链"]
AppTask --> PerfInit["perf_counter_init()"]
PerfInit --> SysTimer["sys_timer_init() / sys_timer_task_init()"]
SysTimer --> EarlyInit["__do_initcall(early_initcall)"]
EarlyInit --> BoardEarly["board_early_init()"]
BoardEarly --> PlatformInit["__do_initcall(platform_initcall)"]
PlatformInit --> FlashProtect["norflash 关键区域保护注册"]
FlashProtect --> BoardInit["board_init()"]
BoardInit --> DlogInit["dlog 输出设备初始化"]
DlogInit --> Initcall["__do_initcall(initcall)"]
Initcall --> ModuleInit["__do_initcall(module_initcall)"]
ModuleInit --> AppCore["app_core_init()"]
AppCore --> LateInit["__do_initcall(late_initcall)"]
LateInit --> AppMain["app_main() 应用入口"]
AppMain --> MsgLoop["while(1) os_task_pend(taskq)"]
end
subgraph sg_Debug["调试与保护设施"]
StackCheck["rtos_stack_check_func(60s 周期)"]
MemLeak["malloc_debug_dump(60s 周期)"]
CppRuntime["cpp_run_init()(C++ 运行时)"]
Finsh["finsh_system_init()(命令行调试)"]
end
AppTask --> CppRuntime
AppTask --> StackCheck
AppTask --> MemLeak
AppMain --> Finsh
架构说明:CPU0 负责最底层的硬件引导与唤醒 CPU1;CPU1 完成中断/调试/RTOS 初始化后创建系统启动任务 app_task_handler,该任务按依赖顺序推进 5 级 initcall、板级回调与调试设施,最终调用 app_main() 并进入 taskq 消息循环。调试设施(栈检查、内存泄漏检测、dlog、finsh)以"周期定时器回调 + 初始化钩子"的方式挂接到框架中,与业务逻辑解耦。
启动入口与双核引导
CPU1 从核入口:cpu1_main
cpu1_main() 是所有 AC792N 应用在 CPU1 上的第一段 C 代码,其职责被刻意保持最小化——只做四件事:开启分支预测、初始化中断、初始化调试、启动 RTOS 内核:
void cpu1_main(void)
{
q32DSP(1)->PMU_CON1 &= ~BIT(8); //cpu1开启分支预测功能
__local_irq_disable();
interrupt_init();
debug_init();
#if CPU_CORE_NUM > 1
cpu1_run_flag = 1; //标记CPU1启动,不能挪到位置,否则会出现擦写flash卡死
os_start();
#else
puts("\r\n\n cpu1_run... \r\n\n");
cpu1_run_flag = 1; //标记CPU1启动,不能挪到位置,否则会出现擦写flash卡死
#endif
__local_irq_enable();
#if defined CONFIG_UCOS_ENABLE || (CPU_CORE_NUM == 1)
//在这句话之后 不可用操作系统接口,printf/puts等打印接口,并且需要替换单核专用的system库,
while (1) {
__asm__ volatile("idle");
}
#endif
}
来源:init.c
设计要点:
- 分支预测:
q32DSP(1)->PMU_CON1 &= ~BIT(8)在进入任何复杂逻辑前开启 CPU1 的分支预测,提升整体执行效率; cpu1_run_flag的放置位置是强约束:源码注释明确"不能挪到位置,否则会出现擦写 flash 卡死"。该标志必须在内核启动(os_start())之前置位,CPU0 依赖它判断 CPU1 是否已就绪,从而避免在 flash 操作期间出现双核竞争;- 单核模式:当
CPU_CORE_NUM == 1或启用 uC/OS 时,CPU1 完成标记后直接进入idle指令空转循环——因为此时不可再使用操作系统接口,必须切换到单核专用的 system 库; cpu_assert_debug():断言失败路径会先冲刷 dlog 到 flash、冲刷日志,然后cpu_reset()复位——保证现场信息落盘后再重启,便于离线分析。
系统启动任务:app_task_handler
cpu1_main 只负责内核启动;真正完整的系统初始化由 RTOS 创建的任务 app_task_handler 执行。其初始化序列是框架的核心,严格遵循"时间接口 → 系统定时器 → 早期初始化 → 板级 → 平台 → 保护 → 模块 → 应用"的依赖顺序:
static void app_task_handler(void *p)
{
#ifdef CONFIG_CXX_SUPPORT
void cpp_run_init(void);
cpp_run_init(); //使用c++时,必须先调用该接口进行初始化
#endif
perf_counter_init();//初始化后才可使用 delay_us delay_ms timer_get_ms 等接口
sys_timer_init();
sys_timer_task_init();
#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
sys_timer_add(NULL, rtos_stack_check_func, 60 * 1000);
#endif
#ifdef CONFIG_MEM_LEAK_CHECK_ENABLE
malloc_debug_start();
sys_timer_add(NULL, malloc_debug_dump, 60 * 1000);
#endif
__do_initcall(early_initcall);
board_early_init();
#ifdef CONFIG_SDFILE_EXT_ENABLE
sdfile_ext_mount_init();
#endif
__do_initcall(platform_initcall);
// norflash关键区域添加擦写保护,需要放到vm初始化后进行
norflash_key_addr_info_init();
norflash_protect_opt_register(norflash_protect_opt);
puts("flash core area protect init\n");
board_init();
...
__do_initcall(initcall);
__do_initcall(module_initcall);
app_core_init();
__do_initcall(late_initcall);
app_main();
...
while (1) {
res = os_task_pend("taskq", msg, ARRAY_SIZE(msg));
...
}
}
来源:init.c
关键顺序语义(WHY):
cpp_run_init()必须最先:一旦启用CONFIG_CXX_SUPPORT,C++ 的全局构造/析构需要运行时支撑,必须先于任何 C++ 对象使用;perf_counter_init()先于定时器:sys_timer_init依赖性能计数器提供的时间基准;初始化完成后才能安全使用delay_us/delay_ms/timer_get_ms;- norflash 保护在
platform_initcall之后:源码注释明确指出"需要放到 vm 初始化后进行",因为关键区域地址信息依赖 VM 已挂载 flash 分区; app_core_init()在module_initcall与late_initcall之间:应用核心(如消息中心、事件系统)就绪后再执行最晚一级的初始化,保证依赖应用核心的模块可用;app_main()是应用真正入口,由各应用(wifi_bbm/wifi_camera/wifi_soundbox 等)分别实现,框架通过extern void app_main(void);声明引用,实现"框架-应用"的编译期绑定。
initcall 分级机制
框架通过 __do_initcall(level) 宏按级别批量执行注册的初始化函数。级别从早到晚依次为:
| 级别 | 执行时机 | 典型用途 |
|---|---|---|
early_initcall | 板级早期初始化前 | 最基础的硬件资源、时钟、内存段 |
platform_initcall | board_early_init 后、norflash 保护前 | 平台外设、存储、VM 等 |
initcall | 应用核心初始化前 | 常规模块驱动 |
module_initcall | app_core_init() 前 | 依赖应用核心框架的模块 |
late_initcall | app_core_init() 后 | 最后收尾、依赖完整运行环境的初始化 |
这种"声明式注册 + 框架统一执行"的机制让新增模块无需修改启动文件:模块只需在自己的源文件中用对应宏注册初始化函数,链接器将其放入专属段,__do_initcall 按段顺序统一调用。这是嵌入式 Linux 内核 initcall 思想的精简移植,也是本框架可扩展性的核心。
核心启动流程
sequenceDiagram
participant ROM as 芯片复位/引导
participant CPU0 as CPU0 主核
participant CPU1 as CPU1 从核
participant RTOS as RTOS 内核
participant APP as app_task_handler
participant DEV as 外设/模块
ROM->>CPU0: 复位向量进入 main
CPU0->>CPU1: 唤醒并启动 cpu1_main
CPU1->>CPU1: 开启分支预测/关中断
CPU1->>CPU1: interrupt_init() + debug_init()
CPU1->>CPU0: cpu1_run_flag = 1
CPU1->>RTOS: os_start()
RTOS->>APP: 创建系统启动任务
APP->>APP: cpp_run_init()(若启用 C++)
APP->>APP: perf_counter_init() + sys_timer_init()
APP->>DEV: __do_initcall(early_initcall)
APP->>DEV: board_early_init()
APP->>DEV: __do_initcall(platform_initcall)
APP->>DEV: norflash 关键区域保护注册
APP->>DEV: board_init() + dlog 输出初始化
APP->>DEV: __do_initcall(initcall)
APP->>DEV: __do_initcall(module_initcall)
APP->>APP: app_core_init()
APP->>DEV: __do_initcall(late_initcall)
APP->>APP: app_main()(应用业务初始化)
APP->>APP: finsh_system_init()(可选)
APP->>APP: while(1) os_task_pend("taskq", msg)
Note over APP: 进入事件驱动消息循环,应用生命周期开始
时序要点:CPU1 在 os_start() 前通过 cpu1_run_flag 与 CPU0 完成握手;app_task_handler 在 RTOS 中作为一个普通任务运行,其初始化全部串行完成,最后驻留于 taskq 消息队列等待事件。该队列由 os_task_pend 挂起,os_task_post 投递,构成应用层事件驱动的统一入口。
运行时支撑设施
errno / jiffies / sysconf
框架在系统层提供了 POSIX 风格的运行时基础:全局 errno 与 __errno() 访问器、全局节拍计数 jiffies、以及 sysconf 的处理器数量查询:
long sysconf(int name)
{
if (name == _SC_NPROCESSORS_CONF) {
return CPU_CORE_NUM;
} else {
printf("[%s, %d][%d]is not supported yet\n", __FUNCTION__, __LINE__, name);
}
return -1;
}
int errno;
__attribute__((used)) int *__errno(void)
{
return &errno;
}
volatile unsigned long jiffies = 10;
来源:init.c
设计意图:让依赖 libc/POSIX 的第三方代码(如文件系统、网络栈)能在无完整操作系统支持的 MCU 环境下编译链接并正确运行。__errno 使用 __attribute__((used)) 防止链接器裁剪,确保 errno 符号始终可见。
C++ 运行时 ABI 支撑(init_expand.c)
init_expand.c 集中实现了 C++ 运行时与标准库的"最小可用"支撑,包括纯虚函数/删除虚函数陷阱、atexit 注册、以及常用标准库函数的桩实现:
__attribute__((noreturn))
void __cxa_pure_virtual(void)
{
printf("Need to make sure \"__cxa_pure_virtual\" runs OK!");
while (1);
}
int __cxa_atexit(void (*destructor)(void *), void *arg, void *dso)
{
// 调用这个函数来注册全局变量的析构函数
// 析构全局变量的时候,会调用这里注册的东西
return 0;
}
__cxa_pure_virtual 与 __cxa_deleted_virtual 均为 noreturn 陷阱:一旦调用到纯虚/删除虚函数即打印提示并死循环,把"未定义行为"变成可诊断的明确故障。__cxa_atexit 是全局析构注册的桩——由于嵌入式系统通常不复位销毁全局对象,直接返回 0 即可。swprintf/fgets/fflush/fprintf/vasprintf 等桩函数则保证依赖这些符号的库可以链接通过,其中 fprintf 被重定向到 vprintf(即串口打印),fflush 为空操作——符合无缓冲 printf 的 MCU 语义。
使用示例
示例 1:注册 norflash 关键区域擦写保护
框架提供"注册回调 + 判定钩子"的保护模式:任何模块想保护 flash 关键地址,只需实现判定函数并通过 norflash_protect_opt_register 注册;之后每次擦写操作都会经过 norflash_protect_opt 检查:
extern void norflash_protect_opt_register(int (*protect_opt_func)(u32, u32));
extern int norflash_key_addr_info_init(void);
extern int norflash_key_addr_judge(u32 opt_addr, u32 opt_len);
int norflash_protect_opt(u32 opt_addr, u32 opt_len)
{
if (norflash_key_addr_judge(opt_addr, opt_len)) {
ASSERT(0, "you are operating norflash key addr!!");
// cpu_reset();
// return -1; //如果只希望跳过操作,不重启,返回-1
}
return 0;
}
来源:init.c
用法说明:在 app_task_handler 中,框架先调用 norflash_key_addr_info_init() 加载关键地址表,再 norflash_protect_opt_register(norflash_protect_opt) 注册钩子(必须位于 VM 初始化之后,见 init.c L246-L248)。命中关键地址时默认 ASSERT(0) 停机;如需"跳过操作继续运行",可取消注释 return -1 分支。
示例 2:周期任务挂接(栈检查 / 内存泄漏检测)
框架通过 sys_timer_add 将调试任务注册为周期回调,无需修改启动流程即可启用运行时诊断:
#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
static void rtos_stack_check_func(void *p)
{
#if defined CONFIG_UCOS_ENABLE
void task_info_output(int size);
task_info_output(0);
int usage[CPU_CORE_NUM] = {0};
os_cpu_usage(NULL, usage);
for (int i = 0; i < CPU_CORE_NUM; ++i) {
printf("cpu%d use: %d%%\n", i, usage[i]);
}
#endif
get_task_state(); //1分钟以内调用一次才准确
dump_cpu_irq_usage();
/* dump_os_sw_cnt(); */
malloc_stats();
extern void efficiency_calculate_show(void);
efficiency_calculate_show();
}
#endif
来源:init.c
注册方式(60 秒周期):
#ifdef CONFIG_RTOS_STACK_CHECK_ENABLE
sys_timer_add(NULL, rtos_stack_check_func, 60 * 1000);
#endif
来源:init.c
rtos_stack_check_func 输出每个任务栈使用率、CPU 占用率、IRQ 占用统计与堆内存统计;注释"1 分钟以内调用一次才准确"提示 get_task_state 的采样周期约束——这是周期设为 60s 的原因。
示例 3:弱符号板级回调扩展
框架将板级钩子声明为 weak 符号,允许各应用选择性覆盖而无需修改框架源码:
void __attribute__((weak)) board_init() {}
void __attribute__((weak)) board_early_init() {}
来源:init.c
设计意图:board_early_init 在 early_initcall 之后、platform_initcall 之前调用,适合极早期的板级资源(如电源、时钟树)配置;board_init 在 norflash 保护注册之后、initcall 之前调用,适合依赖存储/VM 的板级外设初始化。若应用未实现,weak 空实现保证链接通过——这是"默认可用、按需覆盖"的扩展模式。
配置选项
框架行为由编译期宏控制(均定义于各应用的 app_config.h / 编译选项中):
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
CPU_CORE_NUM | int | 1/2 | 内核数;> 1 时 CPU1 执行 os_start() 进入多核模式,否则空转 |
CONFIG_UCOS_ENABLE | bool | 关 | 启用 uC/OS;启用后 CPU1 启动完成后进入 idle 空转 |
CONFIG_CXX_SUPPORT | bool | 关 | 启用 C++ 运行时,启动任务最先调用 cpp_run_init() |
CONFIG_RTOS_STACK_CHECK_ENABLE | bool | 关 | 启用栈检查,注册 60s 周期 rtos_stack_check_func |
CONFIG_MEM_LEAK_CHECK_ENABLE | bool | 关 | 启用内存泄漏检测,注册 60s 周期 malloc_debug_dump |
CONFIG_SDFILE_EXT_ENABLE | bool | 关 | 启用扩展文件系统挂载(sdfile_ext_mount_init) |
CONFIG_DEBUG_ENABLE | bool | 开 | 断言失败时冲刷日志后复位(cpu_assert_debug) |
CONFIG_FINSH_ENABLE | bool | 关 | 启用 finsh 命令行,app_main 后执行 finsh_system_init() |
TCFG_DEBUG_DLOG_ENABLE | bool | 关 | 启用 dlog 日志系统(flash/UART 双输出) |
TCFG_DLOG_OUTPUT_TYPE | enum | - | dlog 输出类型(flash 缓存 / UART) |
TCFG_RF_FCC_TEST_ENABLE | bool | 关 | FCC 测试模式;命中后初始化失败则死循环等待 |
TCFG_RF_PRODUCT_TEST_ENABLE | bool | 关 | 产测模式,同 FCC 分支处理 |
TCFG_EXT_RF_FCC_TEST_ENABLE | bool | 关 | 外部 FCC 工具初始化分支 |
CONFIG_DEMO_UI_PROJECT_ENABLE | bool | 关 | AWTK UI 工程;启用时跳过 cpp_run_init 以避免卡顿 |
API 参考
cpu1_main(void): void
CPU1 从核入口。开启分支预测、初始化中断与调试、置位 cpu1_run_flag、启动 RTOS 内核或进入单核空转。由引导代码直接调用,应用层不应调用。
cpu_assert_debug(void): void
断言失败处理:冲刷 dlog 到 flash(若启用)、冲刷日志、关中断并 cpu_reset() 复位。保证崩溃现场可追溯。
cpu_assert(char *file, int line, bool condition, char *cond_str): void
断言接口(当前为空实现,供编译期引用)。
app_task_handler(void *p): void
系统启动任务。串行执行 C++ 运行时、性能计数器、系统定时器、5 级 initcall、板级回调、norflash 保护、dlog 初始化、FCC/产测分支,最后调用 app_main() 并驻留 taskq 消息循环。
__do_initcall(level): void
执行指定级别(early_initcall/platform_initcall/initcall/module_initcall/late_initcall)下所有注册的初始化函数。
norflash_protect_opt(u32 opt_addr, u32 opt_len): int
norflash 擦写保护回调:命中关键地址则 ASSERT(0)(可改为 return -1 跳过操作)。返回值 0 表示允许操作。
norflash_protect_opt_register(int (*protect_opt_func)(u32, u32)): void
注册 norflash 保护回调,框架在每次擦写操作前调用。
sysconf(int name): long
POSIX 系统配置查询,当前仅支持 _SC_NPROCESSORS_CONF(返回 CPU_CORE_NUM),其余参数打印"not supported yet"并返回 -1。
__cxa_pure_virtual(void): void / __cxa_deleted_virtual(void): void
C++ 纯虚/删除虚函数陷阱,noreturn,打印提示后死循环。
__cxa_atexit(void (*destructor)(void *), void *arg, void *dso): int
C++ 全局析构注册桩,恒返回 0(嵌入式场景不复位销毁全局对象)。
失败模式、边界情况与并发
启动期失败
- FCC/产测初始化失败:当
TCFG_RF_FCC_TEST_ENABLE || TCFG_RF_PRODUCT_TEST_ENABLE且rf_fcc_test_init()返回非 0 时,启动任务进入while(1) os_time_dly(10)死循环——系统停在测试模式而非继续正常启动,这是刻意的"测试优先"设计,防止未通过 RF 校准的设备进入业务运行。 - C++ 纯虚/删除虚函数调用:触发
__cxa_pure_virtual/__cxa_deleted_virtual后打印提示并死循环。虽然不如异常优雅,但在无 MMU/异常处理的 MCU 环境中,死循环+日志是唯一可诊断的失败表达。 CONFIG_DEMO_UI_PROJECT_ENABLE(AWTK):框架主动跳过cpp_run_init()。源码注释说明 AWTK 的第三方库 agge 初始化耗时极长("waitting 50s"),跳过以规避启动卡顿——这是对第三方 UI 库性能问题的框架级妥协。
并发与同步
- 双核握手:
cpu1_run_flag是 CPU0/CPU1 之间的启动同步点。源码明确禁止移动其置位位置,否则"会出现擦写 flash 卡死"——因为 CPU0 可能在 CPU1 尚未完成中断/调试初始化时就发起 flash 操作,导致总线竞争。这属于严格时序约束的共享标志,非原子操作也安全,因为置位发生在 RTOS 启动前的单核上下文。 - 初始化全部串行:5 级 initcall 在同一任务内顺序执行,天然无并发问题;这保证了初始化期间不存在多任务竞争,代价是启动耗时线性累加。
- 消息循环单消费者:
os_task_pend("taskq", msg, ...)由单个应用任务消费,事件按队列顺序处理,避免应用层自建锁。
边界情况
- 单核模式(
CPU_CORE_NUM == 1):cpu1_main不启动 RTOS,置位标志后直接idle空转,且注释要求"替换单核专用的 system 库"——多核代码若在单核构建下运行,须确认所链接 system 库与配置匹配。 - 栈检查采样周期:
get_task_state的统计在 1 分钟以内调用一次才准确,因此周期固定为 60s;缩短周期会导致 CPU 占用统计失真。 - norflash 保护注册时机:必须晚于 VM 初始化(
platform_initcall之后),否则关键地址表无法从已挂载分区加载。
性能与运维
- 启动路径热点:启动时间主要由 initcall 总量、
cpp_run_init(若启用 C++)与 dlog flash 冲刷(dlog_flush_all_cache_and_clear(-1)为无限等待)决定。调试期可临时关闭TCFG_DEBUG_DLOG_ENABLE以缩短启动时间。 - 周期诊断任务开销:栈检查与内存泄漏检测均为 60s 周期,且
malloc_stats、dump_cpu_irq_usage等打印集中在回调内,对运行期性能影响可忽略;但高频打印会占用 UART 带宽,量产版本应通过配置宏关闭。 - 调试日志双通道:dlog 支持 UART 与 flash 双输出;
dlog_flush_all_cache_and_clear在启动期将缓存写入 flash,保证早期日志不丢失。cpu_assert_debug在复位前再次冲刷,形成"崩溃现场落盘"的运维闭环。 - 堆监控:
malloc_debug_start+malloc_debug_dump提供堆泄漏追踪能力,配合malloc_dump可在长稳测试中定位泄漏点。
扩展点
- 新增 initcall 模块:在任意源文件中用
early_initcall/platform_initcall/initcall/module_initcall/late_initcall宏注册初始化函数,框架自动按序调用——这是最常见的扩展方式,新增驱动/模块无需改动启动文件。 - 弱符号板级钩子:实现
board_early_init()/board_init()覆盖 weak 空实现,插入板级定制初始化。 - 周期任务注册:复用
sys_timer_add挂接自定义周期任务(与栈检查/内存泄漏检测同机制)。 - norflash 保护钩子:通过
norflash_protect_opt_register注入自定义擦写保护策略(如整片保护、按地址区间保护)。 - finsh 命令行:启用
CONFIG_FINSH_ENABLE后可在app_main之后进入交互式调试环境,用于运行期探查。
测试与验证
仓库中未发现针对系统启动框架的独立单元测试文件;该框架的验证主要依赖:
- 编译期开关矩阵:
CPU_CORE_NUM、CONFIG_UCOS_ENABLE、CONFIG_CXX_SUPPORT等宏的不同组合构成各应用的实际构建配置,框架须在所有组合下可链接(weak 符号与__cxa_*桩正是为此服务); - 运行时诊断输出:
rtos_stack_check_func的栈/CPU 使用率、malloc_stats堆信息、efficiency_calculate_show效率统计构成启动与长稳测试的观测面; - 断言与保护:
cpu_assert_debug的复位前日志冲刷与 norflash 关键地址保护,为故障注入测试提供确定性行为。
Related Links
- 系统任务模型(task.h / os_task_pend)——
taskq消息队列的接口定义 - init.h——initcall 宏与
__do_initcall声明 - app_core.h——
app_core_init()应用核心框架 - init_expand.c——C++ ABI 与标准库桩实现
- cpp_run_init.c——C++ 运行时初始化入口
- 各应用入口
app_main()实现(如sdk/apps/wifi_bbm/、sdk/apps/wifi_camera/、sdk/apps/wifi_soundbox/)——应用层业务,属于各自应用页面范围