當前位置:編程學習大全網 - 源碼下載 - ucos 怎樣確定任務堆棧大小

ucos 怎樣確定任務堆棧大小

1、首先需要知道,μC/OS-II中創建任務的函數有兩個: OSTaskCreate()和OSTaskCreateExt()

(1)OSTaskCreate()?//創建普通任務?

由於重點在下面的創建擴展任務函數,故本函數就不多說了!確實,要想實現檢測目標任務棧實際使用情況的功能,是不能使用這個函數來創建目標任務的,必須使用OSTaskCreateExt() 。

(2)OSTaskCreateExt() //創建擴展任務

函數接口原型為:

#if OS_TASK_CREATE_EXT_EN > 0

INT8U OSTaskCreateExt

(

void (*task)(void *pd), //建立擴展任務(任務代碼指針

void *pdata, //傳遞參數指針

OS_STK *ptos, //分配任務堆棧棧頂指針

INT8U prio, //分配任務優先級

INT16U id, //(未來的)優先級標識(與優先級相同)

OS_STK *pbos, //分配任務堆棧棧底指針

INT32U stk_size, //指定堆棧的容量(檢驗用)

void *pext, //指向用戶附加的數據域的指針

INT16U opt //建立任務設定選項

)

#endif

2、其次需要知道μC/OS-II中有這麽個函數:OSTaskStkChk()

不錯,檢測任務堆棧實際使用情況正是用的這個函數,下面來本函數的接口原型:

INT8U OSTaskStkChk

(

INT8U prio,? //待測任務的優先級

OS_STK_DATA *pdata //指向壹個類型為OS_STK_DATA的結構體

)

3、再次需要知道壹個結構體:

#if OS_TASK_CREATE_EXT_EN > 0

typedef struct

{

INT32U OSFree; //堆棧中未使用的字節數

INT32U OSUsed; //堆棧中已使用的字節數

} OS_STK_DATA;

#endif

參數: prio 為指定要獲取堆棧信息的任務優先級,也可以指定參數OS_PRIO_SELF,獲取調用任務本身的

信息。

pdata 指向壹個類型為OS_STK_DATA的數據結構,其中包含如下信息:

INT32U OSFree; // 堆棧中未使用的字節數

INT32U OSUsed; // 堆棧中已使用的字節數

4、有了上述三個知識點後就可以啦,具體方法為:

(1)將函數的最後壹個參數opt 設置為:OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR

(2)定義壹個變量:OS_STK_DATA? StackBytes;

(3)調用函數OSTaskStkChk(TestTaskPRIO,?&StackBytes)

(4)StackBytes.OSFree的值即為被測任務堆棧未使用的字節數,

StackBytes.OSUsed的值即為被測任務堆棧已使用的字節數。

5、需要設置宏:OS_TASK_OPT_STK_CLR為1

6、最後壹點建議:

(1)將被測任務經歷最壞的堆棧使用狀態,測出來的使用率才可靠

(2)堆棧使用率最好在%50~%80之間,太小浪費空間,太大不安全

(3)最好在工程中單獨建立壹個優先級較低延時較長的任務來測試其它任務的堆棧使用情況,不用時可以掛起該任務

有時候決定任務實際所需的堆棧空間大小是很有必要的。因為這樣用戶就可以避免為任務分配過多的堆棧空間,從而減少自己的應用程序代碼所需的RAM(內存)數量。?C/OS-Ⅱ提供的OSTaskStkChk()函數可以為用戶提供這種有價值的信息。

在圖4.2中,筆者假定堆棧是從上往下遞減的(即OS_STK_GROWTH被置為1),但以下的討論也同樣適用於從下往上長的堆棧[F4.2(1)]。?C/OS-Ⅱ是通過查看堆棧本身的內容來決定堆棧的方向的。只有內核或是任務發出堆棧檢驗的命令時,堆棧檢驗才會被執行,它不會自動地去不斷檢驗任務的堆棧使用情況。在堆棧檢驗時,?C/OS-Ⅱ要求在任務建立的時候堆棧中存儲的必須是0值(即堆棧被清零)[F4.2(2)]。另外,?C/OS-Ⅱ還需要知道堆棧棧底(BOS)的位置和分配給任務的堆棧的大小[F4.2(2)]。在任務建立的時候,BOS的位置及堆棧的這兩個值儲存在任務的OS_TCB中。

為了使用?C/OS-Ⅱ的堆棧檢驗功能,用戶必須要做以下幾件事情:

l?在OS_CFG.H文件中設OS_TASK_CREATE_EXT為1。

l?用OSTaskCreateExt()建立任務,並給予任務比實際需要更多的內存空間。

l?在OSTaskCreateExt()中,將參數opt設置為OS_TASK_OPT_STK_CHK+OS_TASK_OPT_STK_

CLR。註意如果用戶的程序啟動代碼清除了所有的RAM,並且從未刪除過已建立了的任務,那麽用戶就不必設置選項OS_TASK_OPT_STK_CLR了。這樣就會減少OSTaskCreateExt()的執行時間。

l?將用戶想檢驗的任務的優先級作為OSTaskStkChk()的參數並調用之。

圖?4.2?堆棧檢驗

OSTaskStkChk()順著堆棧的棧底開始計算空閑的堆棧空間大小,具體實現方法是統計儲存值為0的連續堆棧入口的數目,直到發現儲存值不為0的堆棧入口[F4.2(5)]。註意堆棧入口的儲存值在進行檢驗時使用的是堆棧的數據類型(參看OS_CPU.H中的OS_STK)。換句話說,如果堆棧的入口有32位寬,對0值的比較也是按32位完成的。所用的堆棧的空間大小是指從用戶在OSTaskCreateExt()中定義的堆棧大小中減去了儲存值為0的連續堆棧入口以後的大小。OSTaskStkChk()實際上把空閑堆棧的字節數和已用堆棧的字節數放置在0S_STK_DATA數據結構中(參看?COS_Ⅱ.H)。註意在某個給定的時間,被檢驗的任務的堆棧指針可能會指向最初的堆棧棧頂(TOS)與堆棧最深處之間的任何位置[F4.2(7)]。每次在調用OSTaskStkChk()的時候,用戶也可能會因為任務還沒觸及堆棧的最深處而得到不同的堆棧的空閑空間數。

用戶應該使自己的應用程序運行足夠長的時間,並且經歷最壞的堆棧使用情況,這樣才能得到正確的數。壹旦OSTaskStkChk()提供給用戶最壞情況下堆棧的需求,用戶就可以重新設置堆棧的最後容量了。為了適應系統以後的升級和擴展,用戶應該多分配10%-100%的堆棧空間。在堆棧檢驗中,用戶所得到的只是壹個大致的堆棧使用情況,並不能說明堆棧使用的全部實際情況。

OSTaskStkChk()函數的代碼如程序清單?L4.10所示。0S_STK_DATA(參看?COS_Ⅱ.H)數據結構用來保存有關任務堆棧的信息。筆者打算用壹個數據結構來達到兩個目的。第壹,把OSTaskStkChk()當作是查詢類型的函數,並且使所有的查詢函數用同樣的方法返回,即返回查詢數據到某個數據結構中。第二,在數據結構中傳遞數據使得筆者可以在不改變OSTaskStkChk()的API(應用程序編程接口)的條件下為該數據結構增加其它域,從而擴展OSTaskStkChk()的功能。現在,0S_STK_DATA只包含兩個域:OSFree和OSUsed。從代碼中用戶可看到,通過指定執行堆棧檢驗的任務的優先級可以調用OSTaskStkChk()。如果用戶指定0S_PRIO_SELF[L4.10(1)],那麽就表明用戶想知道當前任務的堆棧信息。當然,前提是任務已經存在[L4.10(2)]。要執行堆棧檢驗,用戶必須已用OSTaskCreateExt()建立了任務並且已經傳遞了選項OS_TASK_OPT_CHK[L4.10(3)]。如果所有的條件都滿足了,OSTaskStkChk()就會象前面描述的那樣從堆棧棧底開始統計堆棧的空閑空間[L4.10(4)]。最後,儲存在0S_STK_DATA中的信息就被確定下來了[L4.10(5)]。註意函數所確定的是堆棧的實際空閑字節數和已被占用的字節數,而不是堆棧的總字節數。當然,堆棧的實際大小(用字節表示)就是該兩項之和。

程序清單?L 4.10堆棧檢驗函數

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata)

{

OS_TCB?*ptcb;

OS_STK?*pchk;

INT32Ufree;

INT32Usize;

pdata->OSFree = 0;

pdata->OSUsed = 0;

if (prio > OS_LOWEST_PRIO && prio != OS_PRIO_SELF) {

return (OS_PRIO_INVALID);

}

OS_ENTER_CRITICAL();

if (prio == OS_PRIO_SELF) {?(1)

prio = OSTCBCur->OSTCBPrio;

}

ptcb = OSTCBPrioTbl[prio];

if (ptcb == (OS_TCB *)0) {(2)

OS_EXIT_CRITICAL();

return (OS_TASK_NOT_EXIST);

}

if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0) {(3)

OS_EXIT_CRITICAL();

return (OS_TASK_OPT_ERR);

}

free = 0;?(4)

size = ptcb->OSTCBStkSize;

pchk = ptcb->OSTCBStkBottom;

OS_EXIT_CRITICAL();

#if OS_STK_GROWTH == 1

while (*pchk++ == 0) {

free++;

}

#else

while (*pchk-- == 0) {

free++;

}

#endif

pdata->OSFree = free * sizeof(OS_STK);?(5)

pdata->OSUsed = (size - free) * sizeof(OS_STK);

return (OS_NO_ERR);

}

uCOS-III任務堆棧溢出檢測及統計任務堆棧使用量的方法?

1. 在操作系統任務設計的時候,通常會遇到壹個比較麻煩的問題,也就是任務?堆?棧大小設定的問題,為此我們我需要知道壹些問題?:

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的做法,以保證任務的正常運行。

2. 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%時就必須做出警告了!下面就來介紹任務棧使用量的獲取。

圖1

3. uCOS-III任務堆棧使用量統計的原理和方法

3.1 原理

原理其實很簡單,就是統計連續為0的區域的大小就可以知道空閑棧free的大小,而任務在創建時任務棧總量TaskStkSize是確定的,那麽使用了的棧大小used =?TaskStkSize ?- free。見圖2。

圖2?

首先,當任務創建時其堆棧被清零。然後,通過壹個低優先級任務,計算該任務整個堆棧中值為0的內存大小。如果發現都不為0,那麽就需要擴展堆棧的大小。然後,調整堆棧為的相應大小。這是壹種非常有效的方法。註意的是,程序需用運行很長的時間以讓堆棧達到其需要的最大值。?

3.2 方法?

uC/OS-III提供了壹個函數OSTaskStkChk()用於實現這個計算功能。那麽就來看看具體怎麽做吧。?

3.2.1 首先創建壹個任務來運行任務堆棧統計工作,值得註意的是,這個任務的優先級建議設置為系統所有任務中最低的壹個!

#define ?SystemDatasBroadcast_PRIO 12 // 統計任務優先級最低,我這裏是12,已經低於其他任務的優先級了#define ?SystemDatasBroadcast_STK_SIZE ? 100 // 任務的堆棧大小,做統計壹般夠了,統計結果出來後不夠再加..OS_TCB ?SystemDatasBroadcast_TCB; // 定義統計任務的TCBCPU_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()中開始統計各個任務?(其他任務的代碼就不貼了,?跟

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); 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實驗結果

上述代碼的實驗結果如下圖所示,可以清楚的看到每個任務的堆棧的使用狀況。

但是請遵循壹個原則:必須讓系統運行足夠久,比如盡量讓系統處於不同的運行狀態下,然後觀察任務堆棧使用的變化,找到堆棧的最高使用率,然後根據上文所說的原則按需重新分配新的任務堆棧大小。

  • 上一篇:Mariadb存儲源代碼分析
  • 下一篇:淘寶導航css代碼
  • copyright 2024編程學習大全網