uCOS-III任務堆疊溢位檢測及統計任務堆疊使用量的方法【轉載】
此文章轉載於點選進入原創地址
uCOS-III任務堆疊溢位檢測及統計任務堆疊使用量的方法
- 在作業系統任務設計的時候,通常會遇到一個比較麻煩的問題,也就是任務堆疊大小設定的問題,為此我們我需要知道一些問題:
1.1. 任務堆疊一但溢位,意味著系統的崩潰,在有MMU或者MPU的系統中,對堆疊溢位的檢測十分簡單,因為這是MMU和MPU必備的功能之一。(uCOS-II/uCOS-III中均有針對沒有MMU和MPU的處理器對堆疊溢位檢測的策略)
1.2. 堆疊的大小取決於該任務的需求。設定堆疊大小時,你就需要考慮:所有可能被堆疊呼叫的函式及其函式的巢狀層數,相關區域性變數的大小,中斷服務程式所需要的空間。另外,堆疊還需存入CPU暫存器,如果處理器有浮點數單元FPU暫存器的話還需存入FPU暫存器。(PS:出於這點,所以在嵌入式系統中有個潛規則,避免寫遞迴函式)
1.3. 雖然任務堆疊大小可以通過人工計算出來,但是要考慮的太多,而且不能十分精確的計算。比如逐級巢狀被呼叫的函式的引數使用,上下文切換時候的CPU暫存器空間的儲存,中斷時CPU暫存器空間的儲存和中斷處理函式的堆疊空間等等,未免太過麻煩。特別的,當任務中使用了printf()之類引數可變的函式,那麼統計起來就更困難了。所以這種方式怎麼看怎麼不現實。囧 。
1.4. 建議在不是很精確的確定任務堆疊使用大小(stk_size)的情況下,還是採取stk_size乘以1.5或者2.0的做法,以保證任務的正常執行。
- uCOS-III任務堆疊溢位檢測原理
每個任務都有自己的TCB(Task Control Block 任務控制塊),TCB結構定義在uCOS-III原始碼(我使用的是V3.03.00版本)中的os.h中。TCB中有個StkLimitPtr成員。
假設在切換到任務S前,程式碼會檢測將要被載入CPU堆疊指標的值是否超出該任務S的TCB中StkLimitPtr限制。因為軟體不能在溢位時就迅速地做出反應,所以應該設定StkLimitPtr的值儘可能遠離&MyTaskStk[0],保證有足夠的溢位緩衝。如下圖。軟體檢測不會像硬體檢測那樣有效,但也可以防止堆疊溢位。當uC/OS-III從一個任務切換到另一個任務的時候,它會呼叫一個hook函式OSTaskSwHook(),它允許使用者擴充套件上下文切換時的功能。所以,如果處理器沒有硬體支援溢位檢測功能,就可以在該hook函式中新增程式碼軟體模擬該能。
不過我個人的做法是,通常設定StkLimitPtr指向任務棧大小的90%處,然後獲取任務堆疊使用量,如果棧使用率大於90%時就必須做出警告了!下面就來介紹任務棧使用量的獲取。
- uCOS-III任務堆疊使用量統計的原理和方法
3.1 原理
原理其實很簡單,就是統計連續為0的區域的大小就可以知道空閒棧free的大小,而任務在建立時任務棧總量TaskStkSize是確定的,那麼使用了的棧大小used = TaskStkSize - free。
首先,當任務建立時其堆疊被清零。然後,通過一個低優先順序任務,計算該任務整個堆疊中值為0的記憶體大小。如果發現都不為0,那麼就需要擴充套件堆疊的大小。然後,調整堆疊為的相應大小。這是一種非常有效的方法。注意的是,程式需用執行很長的時間以讓堆疊達到其需要的最大值。
見圖2。
3.2 方法
uC/OS-III提供了一個函式OSTaskStkChk()用於實現這個計算功能。那麼就來看看具體怎麼做吧。
3.2.1 首先建立一個任務來執行任務堆疊統計工作,值得注意的是,這個任務的優先順序建議設定為系統所有任務中最低的一個!
#define SystemDatasBroadcast_PRIO 12 // 統計任務優先順序最低,我這裡是12,已經低於其他任務的優先順序了
#define SystemDatasBroadcast_STK_SIZE 100 // 任務的堆疊大小,做統計一般夠了,統計結果出來後不夠再加..
OS_TCB SystemDatasBroadcast_TCB; // 定義統計任務的TCB
CPU_STK SystemDatasBroadcast_STK [SystemDatasBroadcast_STK_SIZE];// 開闢陣列作為任務棧給任務使用
static void AppTaskCreate(void)
{
// .....
// 這是系統建立任務的函式,還有其他任務建立的程式碼,這裡就不貼出了
// .....
OSTaskCreate( (OS_TCB *)&SystemDatasBroadcast_TCB,
(CPU_CHAR *)"SystemDatasBroadcast",
(OS_TASK_PTR ) SystemDatasBroadcast,
(void *) 0,
(OS_PRIO ) SystemDatasBroadcast_PRIO,
(CPU_STK *)&SystemDatasBroadcast_STK[0],
(CPU_STK_SIZE) SystemDatasBroadcast_STK_SIZE/10,/*棧溢位臨界值我設定在棧大小的90%處*/
(CPU_STK_SIZE) SystemDatasBroadcast_STK_SIZE,
(OS_MSG_QTY ) 0,
(OS_TICK ) 0,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *) &err);
}
3.2.2 然後在任務函式SystemDatasBroadcast()中開始統計各個任務的棧使用。
在uCOS-III中提供了函式
void OSTaskStkChk(OS_TCB *p_tcb, CPU_STK_SIZE *p_free, CPU_STK_SIZE *p_used, OS_ERR *p_err);
呼叫上面這個函式就能獲取到指定任務的堆疊使用量。其中
*p_tcb:指向任務的TCB塊
*p_free:任務空閒的堆疊位元組數
*p_used:任務使用的堆疊位元組數
*p_err:函式執行結果程式碼
特別提示,如果想要使用這個功能,那麼必須在os_cfg.h這個作業系統配置檔案中開啟巨集:
#define OS_CFG_STAT_TASK_STK_CHK_EN 1u /* Check task stacks from statistic task
任務函式SystemDatasBroadcast()的程式碼如下:
void SystemDatasBroadcast (void *p_arg)
{
OS_ERR err;
CPU_STK_SIZE free,used;
(void)p_arg;
while(DEF_TRUE)
{
OSTaskStkChk (&SystemDatasBroadcast_TCB,&free,&used,&err);// 把統計任務本身的堆疊使用量也打印出來
// 然後從實驗結果看看我們設定100位元組給它是不是真的合適
printf("SystemDatasBroadcast used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&Core_Page_TCB,&free,&used,&err);
printf("Core_Page used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&GUIActive_TCB,&free,&used,&err);
printf("GUIActive used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&KeyCheck_Process_TCB,&free,&used,&err);
printf("KeyCheck used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&Light_Adjust_TCB,&free,&used,&err);
printf("Light_Adjust used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&Calibrate_Process_TCB,&free,&used,&err);
printf("Calibrate used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
OSTaskStkChk (&Data_Process_TCB,&free,&used,&err);
printf("Data_Process used/free:%d/%d usage:%%%d\r\n",used,free,(used*100)/(used+free));
printf("\r\n\r\n\r\n");
OSTimeDlyHMSM(0,0,5,0,(OS_OPT)OS_OPT_TIME_DLY,(OS_ERR*)&err);
}
}
3.2.3實驗結果
上述程式碼的實驗結果如下圖所示,可以清楚的看到每個任務的堆疊的使用狀況。從結果中我們看到SystemDataBroadcast任務的100位元組的任務棧只用了58位元組,使用率為58%,還有近一半的富餘,100位元組其實是合適了的,而 58X1.5 = 87,58X2.0 = 116, [87,116]之間取一個數,就取100吧,嘿嘿!當然隨著任務功能的增加,堆疊的使用量也會隨之增加,在程式設計除錯階段,最好謹慎的多檢視任務棧使用的情況以便做出調整,當程式設計並測試完成後這些程式碼都可以去掉,然後軟體進入釋出階段。
但是請遵循一個原則:必須讓系統執行足夠久,比如儘量讓系統處於不同的執行狀態下,然後觀察任務堆疊使用的變化,找到堆疊的最高使用率,然後根據上文所說的原則按需重新分配新的任務堆疊大小。
此文章為轉載*******