μC/OS-II的任務(wù)管理
圖 F4.1 內(nèi)存碎片

μC/OS-Ⅱ支持的處理器的堆棧既可以從上(高地址)往下(低地址)長也可以從下往上長(參看4.02,任務(wù)堆棧)。用戶在調(diào)用OSTaskCreate()或OSTaskCreateExt()的時候必須知道堆棧是怎樣長的,因為用戶必須得把堆棧的棧頂傳遞給以上兩個函數(shù),當(dāng)OS_CPU.H文件中的OS_STK_GROWTH置為0時,用戶需要將堆棧的最低內(nèi)存地址傳遞給任務(wù)創(chuàng)建函數(shù),如程序清單4.7所示。
程序清單 L4.7 堆棧從下往上遞增
OS_STKTaskStack[TASK_STACK_SIZE];
OSTaskCreate(task,pdata,TaskStack[0],prio);
當(dāng)OS_CPU.H文件中的OS_STK_GROWTH置為1時,用戶需要將堆棧的最高內(nèi)存地址傳遞給任務(wù)創(chuàng)建函數(shù),如程序清單4.8所示。
程序清單 L4.8 堆棧從上往下遞減
OS_STKTaskStack[TASK_STACK_SIZE];
OSTaskCreate(task,pdata,TaskStack[TASK_STACK_SIZE-1],prio);
這個問題會影響代碼的可移植性。 如果用戶想將代碼從支持往下遞減堆棧的處理器中移植到支持往上遞增堆棧的處理器中的話,用戶得使代碼同時適應(yīng)以上兩種情況。在這種特殊情況下,程序清單L4.7和4.8可重新寫成如程序清單L4.9所示的形式。
程序清單 L4.9 對兩個方向增長的堆棧都提供支持
OS_STKTaskStack[TASK_STACK_SIZE];
#ifOS_STK_GROWTH==0
OSTaskCreate(task,pdata,TaskStack[0],prio);
#else
OSTaskCreate(task,pdata,TaskStack[TASK_STACK_SIZE-1],prio);
#endif
任務(wù)所需的堆棧的容量是由應(yīng)用程序指定的。 用戶在指定堆棧大小的時候必須考慮用戶的任務(wù)所調(diào)用的所有函數(shù)的嵌套情況,任務(wù)所調(diào)用的所有函數(shù)會分配的局部變量的數(shù)目,以及所有可能的中斷服務(wù)例程嵌套的堆棧需求。另外,用戶的堆棧必須能儲存所有的CPU寄存器。
4.3 堆棧檢驗,OSTaskStkChk()
有時候決定任務(wù)實際所需的堆棧空間大小是很有必要的。因為這樣用戶就可以避免為任務(wù)分配過多的堆棧空間,從而減少自己的應(yīng)用程序代碼所需的RAM(內(nèi)存)數(shù)量。μC/OS-Ⅱ提供的OSTaskStkChk()函數(shù)可以為用戶提供這種有價值的信息。
在圖4.2中,筆者假定堆棧是從上往下遞減的(即OS_STK_GROWTH被置為1),但以下的討論也同樣適用于從下往上長的堆棧[F4.2(1)]。μC/OS-Ⅱ是通過查看堆棧本身的內(nèi)容來決定堆棧的方向的。只有內(nèi)核或是任務(wù)發(fā)出堆棧檢驗的命令時,堆棧檢驗才會被執(zhí)行,它不會自動地去不斷檢驗任務(wù)的堆棧使用情況。在堆棧檢驗時,μC/OS-Ⅱ要求在任務(wù)建立的時候堆棧中存儲的必須是0值(即堆棧被清零)[F4.2(2)]。另外,μC/OS-Ⅱ還需要知道堆棧棧底(BOS)的位置和分配給任務(wù)的堆棧的大小[F4.2(2)]。在任務(wù)建立的時候,BOS的位置及堆棧的這兩個值儲存在任務(wù)的OS_TCB中。
為了使用μC/OS-Ⅱ的堆棧檢驗功能,用戶必須要做以下幾件事情:
z 在OS_CFG.H文件中設(shè)OS_TASK_CREATE_EXT為1。
z 用OSTaskCreateExt()建立任務(wù),并給予任務(wù)比實際需要更多的內(nèi)存空間。
z 在OSTaskCreateExt()中, 將參數(shù)opt設(shè)置為OS_TASK_OPT_STK_CHK+OS_TASK_OPT_STK_CLR。注意如果用戶的程序啟動代碼清除了所有的RAM,并且從未刪除過已建立了的任務(wù),那么用戶就不必設(shè)置選項OS_TASK_OPT_STK_CLR了。這樣就會減少OSTaskCreateExt()的執(zhí)行時間。
z 將用戶想檢驗的任務(wù)的優(yōu)先級作為OSTaskStkChk()的參數(shù)并調(diào)用之。
圖 4.2 堆棧檢驗

OSTaskStkChk()順著堆棧的棧底開始計算空閑的堆??臻g大小, 具體實現(xiàn)方法是統(tǒng)計儲存值為0的連續(xù)堆棧入口的數(shù)目,直到發(fā)現(xiàn)儲存值不為0的堆棧入口[F4.2(5)]。注意堆棧入口的儲存值在進行檢驗時使用的是堆棧的數(shù)據(jù)類型(參看OS_CPU.H中的OS_STK)。換句話說,如果堆棧的入口有32位寬,對0值的比較也是按32位完成的。所用的堆棧的空間大小是指從用戶在OSTaskCreateExt()中定義的堆棧大小中減去了儲存值為0的連續(xù)堆棧入口以后的大小。OSTaskStkChk()實際上把空閑堆棧的字節(jié)數(shù)和已用堆棧的字節(jié)數(shù)放置在0S_STK_DATA數(shù)據(jù)結(jié)構(gòu)中(參看μCOS_Ⅱ.H)。注意在某個給定的時間,被檢驗的任務(wù)的堆棧指針可能會指向最初的堆棧棧頂(TOS)與堆棧最深處之間的任何位置[F4.2(7)]。 每次在調(diào)用OSTaskStkChk()的時候, 用戶也可能會因為任務(wù)還沒觸及堆棧的最深處而得到不同的堆棧的空閑空間數(shù)。
用戶應(yīng)該使自己的應(yīng)用程序運行足夠長的時間,并且經(jīng)歷最壞的堆棧使用情況,這樣才能得到正確的數(shù)。一旦OSTaskStkChk()提供給用戶最壞情況下堆棧的需求,用戶就可以重新設(shè)置堆棧的最后容量了。為了適應(yīng)系統(tǒng)以后的升級和擴展,用戶應(yīng)該多分配10%-100%的堆??臻g。在堆棧檢驗中,用戶所得到的只是一個大致的堆棧使用情況,并不能說明堆棧使用的全部實際情況。
OSTaskStkChk()函數(shù)的代碼如程序清單L4.10所示。0S_STK_DATA(參看μCOS_Ⅱ.H)數(shù)據(jù)結(jié)構(gòu)用來保存有關(guān)任務(wù)堆棧的信息。筆者打算用一個數(shù)據(jù)結(jié)構(gòu)來達(dá)到兩個目的。第一,把OSTaskStkChk()當(dāng)作是查詢類型的函數(shù),并且使所有的查詢函數(shù)用同樣的方法返回,即返回查詢數(shù)據(jù)到某個數(shù)據(jù)結(jié)構(gòu)中。第二,在數(shù)據(jù)結(jié)構(gòu)中傳遞數(shù)據(jù)使得筆者可以在不改變OSTaskStkChk()的API(應(yīng)用程序編程接口)的條件下為該數(shù)據(jù)結(jié)構(gòu)增加其它域,從而擴展OSTaskStkChk()的功能?,F(xiàn)在,0S_STK_DATA只包含兩個域:OSFree和OSUsed。從代碼中用戶可看到,通過指定執(zhí)行堆棧檢驗的任務(wù)的優(yōu)先級可以調(diào)用OSTaskStkChk()。如果用戶指定0S_PRIO_SELF[L4.10(1)],那么就表明用戶想知道當(dāng)前任務(wù)的堆棧信息。當(dāng)然,前提是任務(wù)已經(jīng)存在[L4.10(2)]。要執(zhí)行堆棧檢驗,用戶必須已用OSTaskCreateExt()建立了任務(wù)并且已經(jīng)傳遞了選項OS_TASK_OPT_CHK[L4.10(3)]。如果所有的條件都滿足了,OSTaskStkChk()就會象前面描述的那樣從堆棧棧底開始統(tǒng)計堆棧的空閑空間[L4.10(4)]。 最后,儲存在0S_STK_DATA中的信息就被確定下來了[L4.10(5)]。注意函數(shù)所確定的是堆棧的實際空閑字節(jié)數(shù)和已被占用的字節(jié)數(shù),而不是堆棧的總字節(jié)數(shù)。當(dāng)然,堆棧的實際大小(用
評論