4.6.1 断言机制
2026/7/16大约 5 分钟内核组件内核错误处理断言OS_ASSERT
4.6.1 断言机制
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐☆☆☆
前置知识: C 语言宏、调试基础
🎯 学习目标
- 理解断言在嵌入式开发中的作用
- 掌握
OS_ASSERT和OS_ASSERT_EX的使用方法 - 理解断言触发的处理流程
- 能够区分断言和错误码的适用场景
一、断言概念
1.1 什么是断言
断言(Assertion)是一种运行时条件检查机制,用于验证程序运行中"不应该发生"的情况。当断言条件为假时,表示程序出现了不应该出现的错误状态,系统将打印错误信息并进入安全处理流程。
断言的核心思想是:在开发阶段尽早暴露问题——如果某个条件在逻辑上不可能为假,就用断言来验证它。一旦断言失败,就能迅速定位到问题的根源。
1.2 断言 vs 错误码
| 特性 | 断言 | 错误码 |
|---|---|---|
| 处理方式 | 打印信息 + 进入安全处理 | 函数返回错误码 |
| 适用场景 | 不应发生的逻辑错误(编程错误) | 可预见的运行时错误 |
| 可恢复性 | 通常不可恢复 | 调用者可以处理 |
| 性能影响 | 可配置关闭 | 始终存在 |
| 典型用例 | 空指针、参数越界、状态不一致 | 资源不足、超时、权限拒绝 |
原则:用断言检查"不可能发生"的条件(编程错误),用错误码处理"可能发生但可恢复"的错误(运行时错误)。
二、断言 API
断言 API 定义在 kernel/include/os_assert.h 中,依赖 os_assert.h、os_util.h 和 os_safety.h。
2.1 OS_ASSERT — 基本断言
#ifdef OS_USING_ASSERT
#define OS_ASSERT(condition) \
do \
{ \
if (!(condition)) \
{ \
os_kprintf("Assert failed. Condition(%s). [%s][%d]\r\n", \
#condition, __FUNCTION__, __LINE__); \
os_safety_assert_process(); \
} \
} while (0)
#else
#define OS_ASSERT(condition)
#endif功能:
- 检查
condition是否为真 - 若为假,打印断言失败的条件表达式、函数名和行号
- 调用
os_safety_assert_process()进入安全处理流程
输出示例:
Assert failed. Condition(ptr != OS_NULL). [my_function][128]2.2 OS_ASSERT_EX — 带自定义消息的断言
#ifdef OS_USING_ASSERT
#define OS_ASSERT_EX(condition, fmt, ...) \
do \
{ \
if (!(condition)) \
{ \
os_kprintf("Assert failed. " fmt " [%s][%d]\r\n", \
##__VA_ARGS__, __FUNCTION__, __LINE__); \
os_safety_assert_process(); \
} \
} while (0)
#else
#define OS_ASSERT_EX(condition, fmt, ...)
#endif功能:
- 与
OS_ASSERT类似,但允许添加自定义格式化消息 fmt和...参数遵循printf格式- 可以提供更丰富的上下文信息辅助调试
输出示例:
Assert failed. Timer ID invalid: 0x20001234 [timer_start][56]三、配置选项
3.1 启用断言
在 oneos_config.h 中:
#define OS_USING_ASSERT /* 启用断言机制 */3.2 禁用断言
注释掉 OS_USING_ASSERT 宏后,OS_ASSERT 和 OS_ASSERT_EX 都会被展开为空语句,不会产生任何代码和运行时开销。这在生产版本中很有用,可以减少代码体积和提升性能。
// #define OS_USING_ASSERT /* 禁用断言,生产版本中推荐 */四、断言的处理流程
当断言失败时,处理流程如下:
OS_ASSERT(condition) 失败
│
├─ 1. os_kprintf() 打印断言失败信息
│ - 条件表达式
│ - 函数名
│ - 行号
│
└─ 2. os_safety_assert_process()
│
├─ 若 OS_USING_SAFETY_MECHANISM 启用:
│ └─ 调用用户注册的断言钩子(如果有)
│ └─ 钩子未注册或返回后,进入默认处理
│
└─ 若 OS_USING_SAFETY_MECHANISM 未启用:
└─ os_irq_disable() + 无限循环(停止系统)五、代码示例
5.1 基本使用
#include <os_assert.h>
void set_led_state(int led_id, int state)
{
/* 参数合法性检查:led_id 应在有效范围内 */
OS_ASSERT(led_id >= 0 && led_id < MAX_LED_COUNT);
OS_ASSERT(state == LED_ON || state == LED_OFF);
/* 正常业务逻辑 */
gpio_write(led_pins[led_id], state);
}
void *allocate_buffer(size_t size)
{
void *buf = os_malloc(size);
/* 内存分配失败在嵌入式系统中通常是不可恢复的 */
OS_ASSERT(buf != OS_NULL);
return buf;
}5.2 带自定义消息
void start_timer_with_check(os_timer_id timer)
{
/* 带自定义消息的断言,提供更多上下文 */
OS_ASSERT_EX(timer != OS_NULL, "Timer is NULL, cannot start");
OS_ASSERT_EX(os_timer_check_exist(timer), "Timer ID 0x%08X does not exist", timer);
os_timer_start(timer);
}
void check_task_priority(uint8_t priority)
{
OS_ASSERT_EX(priority < OS_TASK_MAX_PRIORITY,
"Priority %d exceeds max %d",
priority, OS_TASK_MAX_PRIORITY - 1);
}5.3 状态一致性检查
typedef enum {
DEV_STATE_IDLE,
DEV_STATE_BUSY,
DEV_STATE_ERROR
} dev_state_t;
static dev_state_t g_dev_state = DEV_STATE_IDLE;
void device_operation(void)
{
/* 操作前确保设备处于空闲状态 */
OS_ASSERT_EX(g_dev_state == DEV_STATE_IDLE,
"Device busy, current state: %d", g_dev_state);
g_dev_state = DEV_STATE_BUSY;
/* 执行设备操作... */
g_dev_state = DEV_STATE_IDLE;
}六、使用建议
- 在开发阶段始终启用断言:断言帮助在开发过程中快速发现和定位 bug
- 断言用于检查内部逻辑一致性:不要在断言中执行有副作用的操作(如
OS_ASSERT(++i < 10)) - 断言条件应该简洁:复杂的条件表达式会降低断言失败时的可读性
- 生产版本可考虑禁用断言:禁用后不会产生任何代码,有助于减小固件体积
- 不要用断言替代错误码:对于可恢复的运行时错误,应使用错误码机制
📝 本节小结
断言机制是 OneOS 错误处理体系的第一层防护,用于在开发调试阶段捕获"不应该发生"的条件。OS_ASSERT 提供基本的条件检查,OS_ASSERT_EX 允许添加自定义消息以提供更丰富的上下文信息。断言失败后会打印错误信息并调用 os_safety_assert_process() 进入安全处理流程。断言可以配置关闭,在生产版本中可以通过禁用断言来减小代码体积。