一、前言與問題
在基于 FreeRTOS 的嵌入式系統中,我使用 STM32F1 開發一個 MQTT 客戶端應用,涉及兩個主要任務:
- ATRecvParser:負責解析 Wi-Fi 模塊的 AT 命令響應(如 OK、ERROR 和 +IPD 數據)。
- MQTT_Client_Task:通過 MQTT 協議連接服務器,訂閱主題并發布消息。
在運行過程中,程序出現以下異常:
- 調用棧跳轉:調試器顯示程序在 prvIdleTask(空閑任務)和 prvCheckTasksWaitingTermination(FreeRTOS 內部清理函數)之間反復跳轉,無法正常運行。
- TCB 異常:任務控制塊(TCB)的 pxTCB 值顯示為 0xA5A5A5A5(未初始化值),表明 TCB 可能損壞。
二、了解空閑任務prvIdleTask、pxTasksWaitingTerminatio、TCB
1、什么是空閑任務-----prvIdleTask
1.調用時間
在沒有其他任務可運行時(沒有任何其他任務處于就緒態時)保持 CPU 忙碌
2.調用例子:
系統中有三個任務:defaultTask、ATRecvParser 和 MQTT_Client_Task。
- defaultTask 調用 osDelay(1),幾乎一直處于阻塞態。
- ATRecvParser 因 HAL_AT_Secv 阻塞。
- MQTT_Client_Task 已刪除,不再調度。
此時,當系統中無其他就緒任務時,調度器將 CPU 分配給 prvIdleTask。
? 3.特征
- 自動創建,優先級最低(通常為 0)。
- prvIdleTask 是一個無限循環任務,包含 for(;;) 循環,定期檢查系統狀態。
- 執行低優先級操作(如電源管理鉤子)。
- 調用 prvCheckTasksWaitingTermination 清理已刪除任務。
- (避免進入低功耗模式,視配置而定)。
二、什么是pxTasksWaitingTerminatio
- prvCheckTasksWaitingTermination:
- 這是 FreeRTOS 內部函數(定義在 tasks.c 中),負責檢查并清理 pxTasksWaitingTermination 列表中的已刪除任務。
- 當任務被 vTaskDelete 標記為待刪除時,FreeRTOS 將其 TCB 加入該列表。prvCheckTasksWaitingTermination 會釋放 TCB 和棧內存,但前提是任務列表有效且資源可用。
- 如果 TCB 指針無效或列表遍歷出錯,函數可能進入死循環或異常狀態。
關系:
- prvIdleTask 是 prvCheckTasksWaitingTermination 的調用者之一。空閑任務在系統空閑時運行,負責清理工作,因此當 MQTT_Client_Task 刪除后,prvIdleTask 會嘗試調用 prvCheckTasksWaitingTermination。
三、TCB
TCB(Task Control Block,任務控制塊) 是 FreeRTOS 中用于管理任務的核心數據結構。它存儲了一個任務的所有關鍵信息,以便 FreeRTOS 調度器能夠正確地創建、調度、暫停或刪除任務。TCB 包含以下主要內容:任務的棧指針。任務狀態:如運行態、就緒態、阻塞態等。任務優先級:決定調度順序。任務名稱:用于調試。
以下是一個TCB的內容:在這里展現的是一個損壞的TCB,正常 TCB:pxTCB 應為有效地址(如 0x20001000),pxTopOfStack 指向棧頂,xStateListItem 包含有效列表項。
問題:
每個任務都有一個獨立的 TCB。那我上面的prvCheckTasksWaitingTermination();有不是一個任務,為什么可以看見他的TCB?
- pxTCB 并不是 prvCheckTasksWaitingTermination 自己的 TCB(因為它不是任務),而是它正在處理的某個已刪除任務(如 MQTT_Client_Task)的 TCB。
- FreeRTOS 的 TCB 是動態分配的,當任務刪除時,TCB 被加入 pxTasksWaitingTermination,等待清理。prvCheckTasksWaitingTermination 從列表中取出 TCB,賦值給局部變量 pxTCB 進行處理。
四、TCB損壞導致pxTasksWaitingTerminatio和prvIdleTask之間跳轉
正常情況下,MQTT_Client_Task 刪除后,ATRecvParser 阻塞,系統中無就緒任務,prvIdleTask 運行并通過 prvCheckTasksWaitingTermination 清理資源,隨后持續運行直到有任務就緒。
TCB 損壞原因:
- 棧溢出:MQTT_Client_Task 的棧(2048 字節)可能不足,溢出覆蓋 TCB 內存。
- 內存不足:堆內存(2032 字節)可能無法分配 TCB 或棧,導致無效指針。
- 任務刪除異常:vTaskDelete(NULL) 在網絡操作未完成時執行,TCB 狀態不一致。
- 互斥鎖死鎖:at_ret_mutex 未解鎖,阻塞 ATSendCmd,影響 mqtt_connect,導致 TCB 未正確更新。
我就是因為刪除的任務重,at_ret_mutex 未解鎖就把任務刪除了,導致資源為釋放,TCB損壞