rtthread - V5.1.0版本 钩子函数 相对于V4.0.3版本做了很大的修改和优化:
旧版本 V4.0.3:
rt_thread_inited_sethook(thread_inited_hook);
rt_thread_deleted_sethook(thread_deleted_hook);
rt_scheduler_sethook(scheduler_hook);
新版本 V5.1.0:
rt_thread_inited_sethook(&init_hook_node);
rt_object_detach_sethook(obj_detach_hook);
rt_scheduler_sethook(scheduler_hook);
在RT-Thread V5.1.0版本中,删除线程的回调钩子函数(hook)机制主要围绕宏定义配置:
// overflow hook
#define RT_USING_OVERFLOW_CHECK
// hook list
#define RT_USING_HOOK
#define RT_HOOK_USING_FUNC_PTR
#define RT_USING_HOOKLIST
// idle thread
#define RT_USING_IDLE_HOOK
#define RT_IDLE_HOOK_LIST_SIZE 4
#define IDLE_THREAD_STACK_SIZE 2048
函数实现:相关rtthread 单元测试例程:
我自己写的项目应用:
extern void rt_thread_inited_sethook(rt_thread_inited_hooklistnode_t node);
static void thread_inited_hook(rt_thread_t thread)
{
rt_kprintf("current thread name: [%s], Object created. \n", thread->parent.name);
}
RT_OBJECT_HOOKLIST_DEFINE_NODE(rt_thread_inited, init_hook_node, thread_inited_hook);
static void obj_detach_hook(struct rt_object *object)
{
rt_kprintf("current thread name: [%s], Object deleted. \n", object->name);
struct rt_timer* timer = rt_container_of(object, struct rt_timer, parent);
struct rt_thread* thread = rt_container_of(timer, struct rt_thread, thread_timer);
rt_free((void*)thread->user_data);
thread->user_data = RT_NULL;
}
void hook_init(void)
{
rt_thread_inited_sethook(&init_hook_node);
rt_object_detach_sethook(obj_detach_hook);
}
线程创建时,会自动把hook链表进行初始化赋值,并在运行时检测和满足条件时调用钩子函数:
栈溢出钩子
宏定义配置:
/* 栈保护方式选择(二选一) */
#define RT_USING_OVERFLOW_CHECK // 启用软件栈溢出检查(硬件保护不足时使用)
// #define RT_USING_HW_STACK_GUARD // 启用硬件栈保护(推荐)
/* 栈生长方向配置(根据 CPU 手册设置) */
#define ARCH_CPU_STACK_GROWS_UPWARD // 栈向上生长:RAM低地址增长(如 ARM Cortex-M)
// #undef ARCH_CPU_STACK_GROWS_UPWARD // 栈向下生长(如 x86)
若选择软件栈溢出检查,确保 rt_scheduler_stack_check
函数逻辑与栈生长方向一致:
钩子注册方式 - 插入代码块 和 函数指针
RT-Thread V5.1.0支持两种钩子注册方式,编译时插入代码块 优先级高于 函数指针方式:
(1)接口注册:函数指针方式(动态注册)
机制:
通过全局函数指针注册钩子,例如线程删除时触发:
// 声明钩子函数指针(object.c)
static void (*rt_object_detach_hook)(struct rt_object *object);
// 注册钩子函数
void rt_object_detach_sethook(void (*hook)(struct rt_object *object)) {
rt_object_detach_hook = hook;
}
使用示例:
用户需在代码中调用rt_object_detach_sethook
注册回调:
void my_detach_hook(struct rt_object *obj) {
// 自定义逻辑,如资源释放
}
// 初始化时注册
rt_object_detach_sethook(my_detach_hook);
(2)锚点:编译时插入代码块(精细控制)
机制:
在rtconfig.h
中通过宏定义直接插入代码到锚点位置,例如调度器钩子:
// 定义锚点插入宏(rtconfig.h)
#define __on_rt_scheduler_hook(from, to) \
my_scheduler_notifier(from, to) // 替换为自定义函数
// 需提前声明函数原型
extern void my_scheduler_notifier(struct rt_thread *from, struct rt_thread *to);
(3)线程删除与钩子触发
删除线程接口:
使用rt_thread_delete
删除线程时,系统会释放资源并触发相关钩子:rt_err_t rt_thread_delete(rt_thread_t thread);
钩子触发流程:
- 线程删除时调用
rt_object_detach
(内核对象分离)。 - 若配置了
RT_USING_HOOK
,触发rt_object_detach_hook
。 - 若使用编译时插入方式,执行用户定义的代码块(如调度器切换通知)。
- 线程删除时调用
// rtconfig.h 中启用钩子并配置
#define RT_USING_HOOK
#define RT_HOOK_USING_FUNC_PTR // 可选,启用函数指针方式
// 编译时插入调度器钩子(rtconfig.h)
#define __on_rt_scheduler_hook(from, to) \
my_thread_delete_hook(from, to)
extern void my_thread_delete_hook(struct rt_thread *from, struct rt_thread *to);
锚点位置__on_后面函数名字,是固定的和rtthread有关?还是任何名字都可以?
在 RT-Thread V5.1.0 中,锚点位置 __on_
后面的函数名字是固定的,与 RT-Thread 内核的预定义锚点名称强相关。用户不能随意自定义该部分名称,否则可能导致钩子机制失效。
(1)锚点名称的固定性
内核预定义锚点:
RT-Thread 内核在关键代码位置(如线程调度、对象操作等)预定义了锚点名称,例如:// 调度器切换线程的锚点
RT_DEFINE_HOOK(rt_scheduler_hook, void(struct rt_thread *from, struct rt_thread *to));
这里的
rt_scheduler_hook
是内核定义的锚点名称,用户必须严格遵循此名称。用户宏定义规则:
用户需在rtconfig.h
中通过#define __on_<锚点名称>(参数列表)
的格式重定义锚点,例如:#define __on_rt_scheduler_hook(from, to) my_scheduler_notifier(from, to)
其中
rt_scheduler_hook
必须与内核预定义的锚点名称完全一致。
RT-Thread V5.1.0 锚点钩子函数列表
锚点名称 | 用途 | 配置示例 |
---|---|---|
rt_scheduler_hook |
线程调度器切换线程时触发 | #define __on_rt_scheduler_hook(from, to) my_scheduler_notifier(from, to) |
rt_object_attach_hook |
内核对象(如线程、信号量)添加到对象管理器时触发 | #define __on_rt_object_attach_hook(obj) my_obj_attach_hook(obj) |
rt_object_detach_hook |
内核对象从对象管理器移除时触发 | #define __on_rt_object_detach_hook(obj) my_obj_detach_hook(obj) |
rt_object_trytake_hook |
线程尝试获取内核对象(如信号量)时触发 | #define __on_rt_object_trytake_hook(obj) my_trytake_hook(obj) |
rt_object_take_hook |
线程成功获取内核对象后触发 | #define __on_rt_object_take_hook(obj) my_take_hook(obj) |
rt_object_put_hook |
线程释放内核对象时触发 | #define __on_rt_object_put_hook(obj) my_put_hook(obj) |
rt_malloc_hook |
从堆内存分配内存块时触发 | #define __on_rt_malloc_hook(ptr, size) my_malloc_hook(ptr, size) |
rt_free_hook |
释放堆内存块时触发 | #define __on_rt_free_hook(ptr) my_free_hook(ptr) |
rt_mp_alloc_hook |
从内存池分配内存块时触发 | #define __on_rt_mp_alloc_hook(mp, block) my_mp_alloc_hook(mp, block) |
rt_mp_free_hook |
释放内存池内存块时触发 | #define __on_rt_mp_free_hook(mp, block) my_mp_free_hook(mp, block) |
rt_interrupt_enter_hook |
进入中断时触发 | #define __on_rt_interrupt_enter_hook() my_irq_enter_hook() |
rt_interrupt_leave_hook |
退出中断时触发 | #define __on_rt_interrupt_leave_hook() my_irq_leave_hook() |
rt_timer_timeout_hook |
定时器超时时触发 | #define __on_rt_timer_timeout_hook(timer) my_timer_hook(timer) |
rt_thread_inited_hook |
线程初始化完成后触发 | #define __on_rt_thread_inited_hook(thread) my_thread_init_hook(thread) |
rt_thread_suspend_hook |
线程挂起时触发 | #define __on_rt_thread_suspend_hook(thread) my_suspend_hook(thread) |
rt_thread_resume_hook |
线程恢复时触发 | #define __on_rt_thread_resume_hook(thread) my_resume_hook(thread) |
注意事项
- 锚点名称必须与内核定义一致,否则钩子无法生效。
- 插入宏优先级高于函数指针,同时使用时插入宏会覆盖函数指针。
- 避免在钩子函数中执行阻塞操作(如
rt_thread_delay
),否则可能导致系统异常。 - 头文件管理:建议在
rtconfig.h
中包含用户钩子头文件,或通过编译选项(如-include user_hook.h
)引入。