4.6.5 异常处理
2026/7/16大约 5 分钟内核组件内核错误处理异常处理HardFaultMemManage
4.6.5 异常处理
📚 本节导读
学习时长: 约 25 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: ARM Cortex-M 异常模型、中断向量表、寄存器组
🎯 学习目标
- 理解嵌入式系统中常见的异常类型
- 掌握 OneOS 异常处理机制的工作原理
- 学会注册自定义异常处理钩子
- 了解异常信息的收集和调试方法
一、异常处理概述
1.1 什么是异常
在 ARM Cortex-M 架构中,异常(Exception)是指打断正常程序流程的事件。当 CPU 执行非法操作(如访问非法地址、执行未定义指令、除零等)时,会触发相应的异常处理程序。
OneOS 在 ARM Cortex-M 架构上支持以下异常类型:
| 异常 | 优先级 | 触发条件 |
|---|---|---|
| HardFault | -1 | 不可恢复的系统错误(默认异常处理) |
| MemManage | 可配置 | 内存保护违规(MPU 违规) |
| BusFault | 可配置 | 总线错误(访问非法地址、未对齐访问) |
| UsageFault | 可配置 | 指令错误(未定义指令、除零、无效状态) |
1.2 异常处理在 OneOS 中的位置
异常发生
│
├─ HardFault_Handler / MemManage_Handler / BusFault_Handler / UsageFault_Handler
│ │
│ ├─ 异常信息收集(寄存器保存、栈帧分析)
│ │
│ └─ os_safety_exception_process()
│ │
│ ├─ 调用用户注册的异常钩子(如果有)
│ │
│ └─ 默认处理
│ │
│ ├─ OS_USING_SAFETY_MECHANISM 启用:由安全机制处理
│ └─ OS_USING_SAFETY_MECHANISM 未启用:关中断 + 死循环
│
└─ 系统停止或复位二、异常处理 API
2.1 异常钩子注册
定义在 kernel/include/os_safety.h 中:
#ifdef OS_USING_SAFETY_MECHANISM
/* 注册异常处理钩子 */
void os_safety_exception_set_hook(void (*hook)(void));2.2 异常处理函数
/* 处理异常 */
void os_safety_exception_process(void);三、ARM Cortex-M 异常详解
3.1 HardFault
HardFault 是默认的异常处理程序,当其他异常处理未被使能或无法处理时触发。
常见原因:
- 访问非法内存地址
- 从非特权模式访问系统控制寄存器
- 堆栈溢出导致数据损坏
- 不可屏蔽中断(NMI)或总线错误升级
3.2 MemManage Fault
内存管理错误,通常与 MPU(内存保护单元)相关。
常见原因:
- 访问 MPU 禁止的内存区域
- 从不可执行区域执行代码
- 数据访问违反 MPU 权限
3.3 BusFault
总线错误,通常由内存访问引起。
常见原因:
- 访问未映射的内存区域
- 未对齐的内存访问(在禁用非对齐访问时)
- 外设总线错误
3.4 UsageFault
指令/用法错误。
常见原因:
- 执行未定义指令
- 除零操作
- 无效的 CPSR 状态
- 未对齐的 PC 加载
四、代码示例
4.1 基本的异常钩子
#include <os_safety.h>
#include <os_util.h>
static void exception_handler_hook(void)
{
/* 获取当前任务信息 */
os_task_id fault_task = os_task_self();
if (fault_task != OS_NULL)
{
os_kprintf("!!! EXCEPTION IN TASK: %s !!!\r\n",
os_task_get_name(fault_task));
/* 打印栈信息 */
void *stack_top = os_task_get_stack_top(fault_task);
void *stack_begin = os_task_get_stack_begin(fault_task);
void *stack_end = os_task_get_stack_end(fault_task);
os_kprintf("Stack top: 0x%08X\r\n", stack_top);
os_kprintf("Stack begin: 0x%08X\r\n", stack_begin);
os_kprintf("Stack end: 0x%08X\r\n", stack_end);
}
/* 记录系统 tick */
os_kprintf("System tick at fault: %d\r\n", os_tick_get_value());
/* 保存故障信息后复位 */
os_hw_reboot();
}
void init_exception_handler(void)
{
os_safety_exception_set_hook(exception_handler_hook);
}4.2 异常信息收集(Cortex-M 示例)
在异常处理程序中,可以利用 ARM Cortex-M 的异常栈帧来收集更多信息:
异常进入时,硬件自动压栈的寄存器顺序(异常栈帧):
高地址:
xPSR (程序状态寄存器)
PC (程序计数器 - 异常触发时的指令地址)
LR (链接寄存器 - 返回地址)
R12
R3
R2
R1
R0 (SP 指向这里)
低地址:/* 在异常处理函数中获取异常栈帧 */
void get_fault_stack_frame(uint32_t *sp)
{
uint32_t stacked_r0 = sp[0];
uint32_t stacked_r1 = sp[1];
uint32_t stacked_r2 = sp[2];
uint32_t stacked_r3 = sp[3];
uint32_t stacked_r12 = sp[4];
uint32_t stacked_lr = sp[5];
uint32_t stacked_pc = sp[6];
uint32_t stacked_psr = sp[7];
os_kprintf("Fault Stack Frame:\r\n");
os_kprintf(" R0 = 0x%08X\r\n", stacked_r0);
os_kprintf(" R1 = 0x%08X\r\n", stacked_r1);
os_kprintf(" R2 = 0x%08X\r\n", stacked_r2);
os_kprintf(" R3 = 0x%08X\r\n", stacked_r3);
os_kprintf(" R12 = 0x%08X\r\n", stacked_r12);
os_kprintf(" LR = 0x%08X\r\n", stacked_lr);
os_kprintf(" PC = 0x%08X\r\n", stacked_pc);
os_kprintf(" xPSR= 0x%08X\r\n", stacked_psr);
}4.3 综合异常处理示例
#include <os_safety.h>
#include <os_util.h>
#include <os_task.h>
typedef struct {
uint32_t tick;
char task_name[OS_NAME_MAX + 1];
uint32_t fault_pc;
uint32_t fault_lr;
} fault_record_t;
static fault_record_t g_fault_record;
static void comprehensive_exception_hook(void)
{
os_task_id fault_task = os_task_self();
/* 记录基本信息 */
g_fault_record.tick = os_tick_get_value();
if (fault_task != OS_NULL)
{
os_strncpy(g_fault_record.task_name,
os_task_get_name(fault_task),
OS_NAME_MAX);
}
/* 保存到非易失存储 */
save_fault_record(&g_fault_record);
/* 输出诊断信息 */
os_kprintf("\r\n");
os_kprintf("╔══════════════════════════════════════╗\r\n");
os_kprintf("║ SYSTEM EXCEPTION ║\r\n");
os_kprintf("╠══════════════════════════════════════╣\r\n");
os_kprintf("║ Tick: %d\r\n", g_fault_record.tick);
os_kprintf("║ Task: %s\r\n", g_fault_record.task_name);
os_kprintf("╚══════════════════════════════════════╝\r\n");
/* 执行复位 */
os_hw_reboot();
}
void init_fault_handler(void)
{
os_safety_exception_set_hook(comprehensive_exception_hook);
}五、异常调试方法
5.1 分析异常寄存器
ARM Cortex-M 提供了多个用于异常分析的寄存器:
| 寄存器 | 说明 |
|---|---|
| CFSR | 可配置故障状态寄存器(包含 UFSR、BFSR、MMFSR) |
| HFSR | HardFault 状态寄存器 |
| DFSR | 调试故障状态寄存器 |
| AFSR | 辅助故障状态寄存器 |
| MMFAR | MemManage 故障地址寄存器 |
| BFAR | BusFault 地址寄存器 |
5.2 调试步骤
- 读取 CFSR 确定异常类型(UsageFault / BusFault / MemManage)
- 读取 HFSR 确认是否被升级为 HardFault
- 读取 BFAR / MMFAR 获取故障地址
- 分析异常栈帧中的 PC 和 LR 定位故障指令
- 反汇编 PC 地址附近的代码,分析故障原因
六、与断言系统的集成
异常处理与断言系统紧密集成:
- 断言失败时,
OS_ASSERT/OS_ASSERT_EX调用os_safety_assert_process() - 异常发生时,异常处理程序调用
os_safety_exception_process() - 两者都使用统一的钩子机制,可以注册相同的处理函数
void init_unified_handler(void)
{
/* 使用统一的故障处理函数 */
os_safety_assert_set_hook(comprehensive_fault_hook);
os_safety_exception_set_hook(comprehensive_fault_hook);
}📝 本节小结
异常处理是 OneOS 错误处理体系中处理硬件级故障的关键组件。在 ARM Cortex-M 架构上,OneOS 支持 HardFault、MemManage、BusFault 和 UsageFault 四种异常类型。通过 os_safety_exception_set_hook 可以注册自定义异常处理钩子,在异常发生时收集故障信息(寄存器、栈帧、任务信息)并执行相应的恢复操作(如系统复位)。异常处理与断言系统共享统一的钩子机制,为系统提供了完整的故障处理链路。