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);
}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
?函數邏輯與棧生長方向一致:
空閑線程鉤子
1.?rt_thread_idle_sethook(void (*hook)(void))
作用:
- 注冊空閑線程鉤子函數:將用戶自定義的函數(
hook
)注冊到系統的空閑線程(idle thread)中。當系統進入空閑狀態(即所有高優先級任務都處于阻塞狀態)時,該鉤子函數會被自動調用。
使用場景:
- 后臺低優先級任務:執行對實時性要求不高的任務,例如:
- 內存回收(如動態內存管理中的垃圾回收)。
- 系統狀態監控(如CPU占用率統計)。
- 低功耗模式處理(如進入睡眠模式前的準備工作)。
- 調試與日志:在空閑時輸出系統運行日志或調試信息。
- 自定義清理操作:如關閉未使用的外設、重置看門狗等。
示例代碼:
void my_idle_hook(void) { |
rt_kprintf("System is idle, performing background tasks...\n"); |
// 執行內存回收或其他操作 |
} |
int main(void) { |
// 注冊空閑鉤子 |
rt_thread_idle_sethook(my_idle_hook); |
return 0; |
} |
2.?rt_thread_idle_delhook(void (*hook)(void))
作用:
- 刪除空閑線程鉤子函數:從空閑線程的鉤子函數列表中移除已注冊的
hook
函數。
使用場景:
- 動態管理鉤子函數:當某個后臺任務不再需要執行時(如臨時調試完成后),通過此函數移除鉤子以釋放資源。
- 避免沖突:在系統配置變更或模塊卸載時,移除不再需要的鉤子函數。
示例代碼:
// 刪除已注冊的鉤子 |
rt_thread_idle_delhook(my_idle_hook); |
關鍵注意事項:
- 非阻塞性:鉤子函數必須是非阻塞的,且執行時間盡可能短,否則會影響系統實時性。
- 多鉤子支持:RT-Thread允許注冊多個鉤子函數,按注冊順序依次執行。
- 線程安全:操作鉤子函數時需確保線程安全(如避免在中斷上下文中注冊/刪除鉤子)。
- 資源管理:若鉤子函數涉及動態內存或資源分配,需自行處理釋放邏輯。
典型應用場景
- 內存管理:在空閑時回收動態內存碎片。
- 低功耗優化:在空閑時關閉未使用的硬件模塊。
- 系統監控:統計任務運行時間或檢測異常狀態。
鉤子注冊方式?-?插入代碼塊 和 函數指針
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
)引入。