FreeRTOS 任務棧溢出檢測
????棧生長方向從高地址向低地址生長(M3、M4)

(1) 上圖標識 1 的位置是 RTOS 的某個任務調用了函數(shù) test()前的 SP 棧指針位置。
(2) 上圖標識 2 的位置是調用了函數(shù) test 需要保存返回地址到??臻g。這一步不是必須的,對于 M3 和 M4 內核是先將其保存到 LR 寄存器中,如果 LR 寄存器中有保存上一級函數(shù)的返回地址,需要將 LR 寄存器中的內容先入棧。
(3) 上圖標識 3 的位置是局部變量 int i 和 int array[10]占用的??臻g,但申請了??臻g后已經越界了。這個就是所謂的棧溢出了。如果用戶在函數(shù) test 中通過數(shù)組 array 修改了這部分越界區(qū)的數(shù)據(jù)且這部分越界的棧空間暫時沒有用到或者數(shù)據(jù)不是很重要,情況還不算嚴重,但是如果存儲的是關鍵數(shù)據(jù),會直接導致系統(tǒng)崩潰。
(4) 上圖標識 4 的位置是局部變量申請了??臻g后,棧指針向下偏移(返回地址+變量 i+10 個數(shù)組元 素)*4 =48 個字節(jié)。
(5) 上圖標識 5 的位置可能是其它任務的??臻g,也可能是全局變量或者其它用途的存儲區(qū),如果 test 函數(shù)在使用中還有用到棧的地方就會從這里申請,這部分越界的空間暫時沒有用到或者數(shù)據(jù)不是很重要,情況還不算嚴重,但是如果存儲的是關鍵數(shù)據(jù),會直接導致系統(tǒng)崩潰。
鉤子函數(shù)
????鉤子函數(shù)的主要作用就是對原有函數(shù)的功能進行擴展,用戶可以根據(jù)自己的需要往里面添加相關的測試代碼,可以在 FreeRTOS 工程中檢索這個鉤子函數(shù) vApplicationStackOverflowHook 所在的位置。
FreeRTOS 的棧溢出檢測機制
FreeRTOS 提供了兩種棧溢出檢測機制,這兩種檢測都是在任務切換時才會進行:?
????◆ 方法一???? 在任務切換時檢測任務棧指針是否過界了,如果過界了,在任務切換的時候會觸發(fā)棧溢出鉤子函數(shù)
void ????vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
????用戶可以在鉤子函數(shù)里面做一些處理。這種方法不能保證所有的棧溢出都能檢測到。
????比如任務在執(zhí)行的過程中出現(xiàn)過棧溢出。任務切換前棧指針又恢復到了正常水平,這種情況在任務切換的時候是檢測不到的。
????又比如任務棧溢出后,把這部分棧區(qū)的數(shù)據(jù)修改了,這部分棧區(qū)的數(shù)據(jù)不重要或者暫時沒有用到還好,但如果是重要數(shù)據(jù)被修改將直接導致系統(tǒng)進入硬件異常,這種情況下,棧溢出檢測功能也是檢測不到的。
使用方法一需要用戶在 FreeRTOSConfig.h 文件中配置如下宏定義:?
????#define configCHECK_FOR_STACK_OVERFLOW ????1
????◆ 方法二 ????任務創(chuàng)建的時候將任務棧所有數(shù)據(jù)初始化為 0xa5,任務切換時進行任務棧檢測的時候會檢測末尾的 16 個字節(jié)是否都是 0xa5,通過這種方式來檢測任務棧是否溢出了。
????相比方法一,這種方法的速度稍慢些,但是這樣就有效地避免了方法一里面的部分情況。不過依然不能保證所有的棧溢出都能檢測到。
????比如任務棧末尾的 16 個字節(jié)沒有用到,即沒有被修改,但是任務棧已經溢出了,這種情況是檢測不到的。
????另外任務棧溢出后,任務棧末尾的 16 個字節(jié)沒有修改,但是溢出部分的棧區(qū)數(shù)據(jù)被修改了,這部分棧區(qū)的數(shù)據(jù)不重要或者暫時沒有用到還好,但如果是重要數(shù)據(jù)被修改將直接導致系統(tǒng)進 入硬件異常,這種情況下,棧溢出檢測功能也是檢測不到的。 使用方法二需要用戶在 FreeRTOSConfig.h 文件中配置如下宏定義:
????#define configCHECK_FOR_STACK_OVERFLOW ????2