cortex_m_irq_hook函数详解
2026/4/29大约 4 分钟初始化初始化中断钩子
cortex_m_irq_hook函数详解
📚 本节导读
用途: 讲解中断钩子函数的工作原理和使用方法
来源: drivers/boot/cotex-m/vector_table.c
一、函数概述
1.1 概念定义
cortex_m_irq_hook() 是 OneOS 中断系统的钩子函数,用于在中断发生时调用所有注册的自定义中断钩子。
这个函数是 Cortex-M 处理器中断处理流程的一部分,允许用户在中断发生时执行自定义代码。
二、函数详解
2.1 完整代码
来源: drivers/boot/cotex-m/vector_table.c (第 63-88 行)
static void cortex_m_irq_hook(int irq)
{
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern const int os_irq_hook$$Base;
extern const int os_irq_hook$$Limit;
const os_irq_hook *hook = (os_irq_hook *)&os_irq_hook$$Base;
int nr = (os_irq_hook *)&os_irq_hook$$Limit - hook;
#elif defined(__GNUC__)
extern const int __os_irq_hook_start;
extern const int __os_irq_hook_end;
const os_irq_hook *hook = (os_irq_hook *)&__os_irq_hook_start;
int nr = (os_irq_hook *)&__os_irq_hook_end - hook;
#endif
int i;
for (i = 0; i < nr; i++)
{
hook[i](irq);
}
}2.2 分步详解
第一步:获取钩子表起始和结束地址
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern const int os_irq_hook$$Base;
extern const int os_irq_hook$$Limit;
const os_irq_hook *hook = (os_irq_hook *)&os_irq_hook$$Base;
int nr = (os_irq_hook *)&os_irq_hook$$Limit - hook;
#elif defined(__GNUC__)
extern const int __os_irq_hook_start;
extern const int __os_irq_hook_end;
const os_irq_hook *hook = (os_irq_hook *)&__os_irq_hook_start;
int nr = (os_irq_hook *)&__os_irq_hook_end - hook;
#endif详细解释:
- 编译器差异:不同编译器使用不同的链接器符号
- ARMCC/Clang:使用
os_irq_hook$$Base和os_irq_hook$$Limit - GCC:使用
__os_irq_hook_start和__os_irq_hook_end - 作用:获取钩子表的起始位置和钩子数量
第二步:遍历调用所有钩子
int i;
for (i = 0; i < nr; i++)
{
hook[i](irq);
}详细解释:
- 遍历:按顺序遍历钩子表中的所有钩子
- 调用:传入中断号
irq,执行钩子函数 - 作用:让所有注册的钩子都有机会在中断发生时执行
三、符号说明
3.1 钩子表符号
| 编译器 | 起始符号 | 结束符号 |
|---|---|---|
| ARMCC/Clang | os_irq_hook$$Base | os_irq_hook$$Limit |
| GCC | __os_irq_hook_start | __os_irq_hook_end |
3.2 符号来源详解
这些符号是由链接器脚本(Linker Script)定义的,用于标记 os_irq_hook 段的起始和结束位置。
GCC 编译器符号来源
在 drivers/link/common.lds(第 93-97 行)中定义:
/* section information for irq hook */
. = ALIGN(4);
__os_irq_hook_start = .;
KEEP(*(os_irq_hook))
__os_irq_hook_end = .;说明:
__os_irq_hook_start:钩子表的起始地址KEEP(*(os_irq_hook)):保留所有位于os_irq_hook段的内容,防止被链接器优化掉__os_irq_hook_end:钩子表的结束地址
ARMCC/Clang 编译器符号
ARM 编译器(ARMCC/Clang)会自动为命名段生成 段名$$Base 和 段名$$Limit 符号,无需在链接器脚本中显式定义。
四、OS_IRQ_HOOK 宏详解
4.1 宏定义
在 drivers/driver.h(第 88-90 行)中定义:
typedef void (*os_irq_hook)(int irq);
#define OS_IRQ_HOOK(__hook) static OS_USED OS_SECTION("os_irq_hook") const os_irq_hook __irq_hook__##__hook = __hook4.2 宏展开解析
以实际使用为例(在 drivers/lpmgr/lpmgr.c 第 346 行):
OS_IRQ_HOOK(os_lpmgr_irq_entry);宏展开后(以 GCC 为例):
static __attribute__((used)) __attribute__((section("os_irq_hook"))) const os_irq_hook __irq_hook__os_lpmgr_irq_entry = os_lpmgr_irq_entry;4.3 关键字/属性详解
| 关键字/属性 | 定义位置 | 作用 |
|---|---|---|
static | C 语言标准 | 限制变量作用域仅在当前文件 |
OS_USED | kernel/include/os_stddef.h 第 46 行 | 告诉编译器/链接器这个变量是被使用的,不要优化掉 |
OS_SECTION("os_irq_hook") | kernel/include/os_stddef.h 第 43 行 | 将变量放置在名为 "os_irq_hook" 的段中 |
const os_irq_hook | drivers/driver.h 第 88 行 | 定义一个函数指针常量 |
__irq_hook__##__hook | 宏拼接 | 生成唯一的变量名,避免命名冲突 |
4.4 编译器段机制工作原理
- 注册阶段:使用
OS_IRQ_HOOK()宏注册钩子函数 - 编译阶段:编译器将所有钩子函数指针放入
os_irq_hook段 - 链接阶段:
- 链接器脚本将
os_irq_hook段的内容连续排列 - 定义
__os_irq_hook_start和__os_irq_hook_end符号标记范围 - 使用
KEEP(*(os_irq_hook))确保不被优化
- 链接器脚本将
- 运行阶段:
cortex_m_irq_hook()函数遍历这个段,调用所有钩子
4.5 使用示例
定义一个中断钩子函数:
/* 定义钩子函数 */
static void my_irq_hook(int irq)
{
/* 每次中断发生时都会执行这里的代码 */
os_kprintf("IRQ %d occurred\n", irq);
}
/* 注册钩子函数 */
OS_IRQ_HOOK(my_irq_hook);五、设计原理
5.1 为什么使用钩子函数?
- 灵活性:允许用户在不修改内核代码的情况下扩展中断处理
- 解耦:将中断处理与用户自定义逻辑分离
- 多钩子支持:可以同时注册多个钩子,按顺序执行
5.2 钩子的执行时机
钩子在中断处理流程中的执行顺序:
- 中断发生
- CPU 跳转到中断向量
- 调用
cortex_m_vector_entry() - 调用
cortex_m_irq_hook()← 这里 - 调用实际的中断处理函数
- 中断返回
六、关键说明
| 功能 | 说明 |
|---|---|
| 函数类型 | 静态函数,仅在 vector_table.c 内可见 |
| 参数 | irq:中断号 |
| 返回值 | 无返回值 |
| 执行时机 | 每次中断发生时 |
| 钩子数量 | 支持多个钩子,按顺序执行 |
提示
钩子函数应该尽量快速,避免影响中断响应的实时性。