<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > uC/OS-II任務(wù)棧處理的一種改進(jìn)方法

uC/OS-II任務(wù)棧處理的一種改進(jìn)方法

作者: 時(shí)間:2004-12-10 來(lái)源:網(wǎng)絡(luò ) 收藏
摘要:在μC/OS-II內核中,各個(gè)不同的使用獨立的堆??臻g,堆棧的大小按每個(gè)所需要的最大堆棧深度來(lái)定義,這種可能會(huì )造成堆??臻g浪費。本文敘述如何在RTOS中多個(gè)共用連續存儲空間作為任務(wù)棧的,并詳細比較二者的優(yōu)缺點(diǎn)和適用性。

關(guān)鍵詞:μC/OS-II 任務(wù)堆棧 RTOS 共用空間堆棧

關(guān)于μC/OS-II這個(gè)實(shí)時(shí)內核及其應用已經(jīng)有很多文章介紹了,對于學(xué)習RTOS的人來(lái)說(shuō),這個(gè)系統是很好的學(xué)習起點(diǎn)。雖然文獻[1]的源代碼沒(méi)有行號和函數名交叉索引表等,給源代碼閱讀造成一些困難(可使用BC31的grep查找功能,提高閱讀效率),好在代碼不是很長(cháng),前面又有詳細的中文說(shuō)明,對于有一定X86匯編和C語(yǔ)言基礎的人來(lái)說(shuō),仍然可以在不長(cháng)的時(shí)間內掌握。

μC/OS-II內核是一個(gè)搶先式內核,可以進(jìn)行任務(wù)間切換,也可以讓一個(gè)任務(wù)在得不到某個(gè)資源時(shí)休眠一定時(shí)間后再繼續運行;提供了用于共享資源管理的信號燈,用于進(jìn)程通信的消息隊列和郵箱,甚至提供了存儲器管理機制,一個(gè)比較全面的系統。

μC/OS-II內核有些地方仍然值得,比如該系統不支持時(shí)間片調度。如果有一個(gè)任務(wù)中一段死循環(huán)代碼(或者條件循環(huán)代碼),代碼就會(huì )永遠(或長(cháng)時(shí)間)在此處執行,調度程序無(wú)法控制,其它任務(wù)也就是不到及時(shí)執行。這種搶先式實(shí)際上和非搶先式系統存在著(zhù)同樣問(wèn)題。當然,如果這種代碼不一個(gè)BUG,問(wèn)題是可以解決的,在不提供時(shí)間片調度的搶先式系統中,一般采取信號燈,或者任務(wù)主動(dòng)休眠的(對于μC/OS-II,很容易改造成支持時(shí)間片調度,只要在定時(shí)中斷服務(wù)程序調用OSIntCtxSw()函數即可);非搶先式系統一般采取有限狀態(tài)機方法,不使用這種耗時(shí)很長(cháng)的循環(huán)代碼。不過(guò),無(wú)論如何,對RTOS的使用者來(lái)說(shuō),這畢竟會(huì )使得任務(wù)函數的編碼不能隨心所欲。

ΜC/OS-II內核的另外一個(gè)值得的地方就是其任務(wù)棧管理方法。在μC/OS-II內核中,各個(gè)不同的任務(wù)使用獨立的堆??臻g,堆棧的大小按每個(gè)任務(wù)所需要的最大堆棧深度來(lái)定義,這種方法可能會(huì )造成堆??臻g的浪費。下面討論如何在RTOS中多個(gè)任務(wù)共用一段連續存儲空間作為傻堆棧。

1 任務(wù)切換要保存的數據

簡(jiǎn)單地說(shuō),一個(gè)任務(wù)可看作一個(gè)運行中的C函數。對于搶先式RTOS來(lái)說(shuō),在任務(wù)切換時(shí),應保存當前任務(wù)的各種現場(chǎng)數據?,F場(chǎng)數據包括局部變量、各個(gè)CPU寄存器、堆棧指針和程序被中止的任務(wù)指針。CPU寄存器是任何任務(wù)代碼均會(huì )用到的;而局部變量,一般的編譯器是將其它安排在堆??臻g中,堆棧指針也是各任務(wù)公用的,所以也需要保存。

對于全局變量,由于一般是在內存中的固定位置,各任務(wù)所占用的空間完全獨立,所以不需要保存。

在X86環(huán)境中,要保存的CPU寄存器共14個(gè)16位寄存器;通用寄存器8個(gè)(AX、BX、CX、DX、SP、BP、SI、BI)、段寄存器4個(gè)(CS、DS、ES、SS)以及指令指針I(yè)P和標志寄存器FR各1個(gè)。

2 C編譯器中變量在堆棧中的位置

對于一個(gè)存在函數調用嵌套的C程序來(lái)說(shuō),大部分編譯器將傳遞的參數和函數本身的局部變量放在了堆棧中,編譯器會(huì )自動(dòng)生成壓棧(push)和彈棧(pop)代碼,以保存上級函數的運行寄存器。

假設函數main()調用funl(),而funl()調用fun2(),則在執行fun2()中的代碼時(shí),堆棧映像如圖1所示(X86 CPU的情況)。

對于RTOS軟件,堆棧中的各種數據就是一個(gè)任務(wù)的作現場(chǎng)。一般CPU的堆棧指針SP只有一個(gè),在進(jìn)行任務(wù)切換時(shí),必須將掛起任務(wù)所使用的堆棧內容保存起來(lái),以便使該任務(wù)在下次喚醒時(shí)能從原地繼續運行。

3 μC/OS-II對任務(wù)棧的方法與缺陷

μC/OS-II為了保存任務(wù)堆棧中的數據,對每個(gè)任務(wù)定義一個(gè)數組變量作為堆棧,在任務(wù)切換時(shí),將CPU堆棧指針SP指向該數組中的某個(gè)元素,即棧頂,如圖2所示。

比如,在其ex21.c文件中定義的任務(wù)堆棧語(yǔ)句為:

OS_STK TaskStartStk[TASK_STK_SIZE]; /*啟動(dòng)任務(wù)堆棧*/

OS_STK TaskClkStk[TASK_STK_SIZE]; /*時(shí)鐘任務(wù)堆棧*/

OS_STK TasklStk[TASK_STK_SIZE]; /*任務(wù)1#,任務(wù)堆棧*/

……

以上各任務(wù)堆棧數組變量在初始化函數OSTCBInit()中被會(huì )給了任務(wù)控制塊OS_TCB的OSTCBStkPtr變量。在任務(wù)切換時(shí),μC/OS-II調用OSCtxSw匯編過(guò)程(OS_CPU_A.ASM文件),將CPU的SP指針指向該變量,從而使每個(gè)任務(wù)使用獨立的任務(wù)堆棧。

LES BX,DWORD PTR DS:_OSTCBCur

;保存掛起任務(wù)的堆棧指針SP

MOV ES:[BX+2],SS

MOV ES:[BX+0],SP

……

LESB X,DWORD PTR DS:_OSTCBHighRdy ;切換SP到要運行任務(wù)的堆??臻g

MOV SS,ES:[BX+2]

MOV SP,ES:[BX]

……

在代碼中,變量OSTCBHighRdy(OSTCBCur)和堆棧指針變量OSTCBStkPtr的數值是同同的,因為OSTCBStkPtr是結構OSTCBHighRdy的第一個(gè)變量。

這種任務(wù)棧方法的缺點(diǎn)是可能造成空間的浪費。因為一個(gè)任務(wù)如果堆棧滿(mǎn)了,該任務(wù)也就無(wú)法運行,即使其它任務(wù)的堆棧還有空間可用。當然,這種方法的好處是任務(wù)棧切換的時(shí)間非常短,只需要幾條指令。

4 共用空間的堆棧方法

(1)棧共用連續存儲空間

如果多個(gè)任務(wù)使用同一段連續空間作為堆棧,這樣各個(gè)堆棧之間就可以互補使用。在前面說(shuō)過(guò),共用空間的問(wèn)題在于一個(gè)任務(wù)運行時(shí)不能破壞其它任務(wù)的堆棧數據。為簡(jiǎn)單起見(jiàn),先看圖3所示兩個(gè)任務(wù)的情況。

假定任務(wù)1首次運行時(shí)任務(wù)棧為空。運行一段時(shí)間后任務(wù)2運行,堆??臻g繼續往上生長(cháng)。這次任務(wù)切換不需要修改CPU的SP數值,但需要記下任務(wù)1的棧頂位置SP1(圖3中)。

在任務(wù)2運行一段時(shí)間后,RTOS又切換到任務(wù)1運行。在切換時(shí),不能簡(jiǎn)單地將SP指針修改回SP1的數值,因為這樣堆棧向上生長(cháng)時(shí)會(huì )破壞任務(wù)2堆棧中的數據。辦法是將原來(lái)任1務(wù)堆棧保存的數據移動(dòng)到靠棧頂的位置,而將任務(wù)2堆棧數據下移到靠棧底的位置,堆棧指針SP實(shí)際上不需要修改(圖3右)。

考慮到更為一般的情況,有N個(gè)任務(wù),當前運行的任務(wù)為k,下一個(gè)運行的任務(wù)為j,在共用任務(wù)堆棧時(shí)必須做的工作有:

*為每個(gè)任務(wù)定義棧頂和棧底2個(gè)堆棧指針;

*在任務(wù)切換時(shí),將待運行任務(wù)j的堆棧內容移動(dòng)到靠棧頂位置,同時(shí)將其堆棧上方的任務(wù)堆棧下移,修改被移動(dòng)推棧的任務(wù)堆棧指針。

假設我們定義的任務(wù)??臻g和任務(wù)的棧指針變量為:

void TaskSTK[MAX_STK_LEN];/*任務(wù)堆??臻g*/

typedef struct TaskSTKPoint{

int TaskID;

int pTopSTK;

int pBottomSTK;

}TASK_STK_POINT;

TASK_STK_POINT pTaskSTK[MAX_TASK_NUM]; /*存放每個(gè)任務(wù)的棧頂和棧底指針*/

任務(wù)棧指針數組pTaskSTK的元素個(gè)數同任務(wù)個(gè)數。為了堆棧交換,需要另外一塊臨時(shí)存儲空間,其大小可按單個(gè)任務(wù)棧最大長(cháng)度定義,用于中轉堆棧交換的內容。堆棧內容交換的偽C算法可寫(xiě)為:

StkEechange(int CurTaskID,int RunTaskID)

{ /*2個(gè)參數為當前運行任務(wù)號和下一運行任務(wù)號*/

void TempSTK[MAX_PER_STK_LEN]; /*注意該變量長(cháng)度可小于TaskSTK*/

L=任務(wù)RunTaskTD的堆棧長(cháng)度;

①將TaskSTK頂部的L字節移動(dòng)到TempSTK中;

②將RunTaskID任務(wù)的堆棧內容移動(dòng)到TaskSTK頂部;

③將RunTaskID堆棧上方(移動(dòng)前位置)所有內容下移L個(gè)字節;

④修改RunTask堆棧上方(移動(dòng)前位置)所有任務(wù)棧頂和棧底指針(pTaskSTK變量);

};

該算法的平均時(shí)間復雜度可計算如下:

O(T)=SL/2+SL/2+SLN/2

式中,第一、二項為步驟①和步驟②時(shí)間,第三項為步驟③時(shí)間;SL表示每個(gè)任堆棧的最大長(cháng)度(即MAX_PER_STK_LEN),N表示任務(wù)數。

取SL為64字節,任務(wù)數為16個(gè),則數據項平均移動(dòng)次數為576。假設每次移動(dòng)指令時(shí)間為2μs,則一次任務(wù)棧移動(dòng)時(shí)間長(cháng)達約1ms。所以在使用該方法時(shí),為了執行時(shí)間盡量短,編碼時(shí)應仔細推敲。

從空間上說(shuō),共用任務(wù)棧比獨立任務(wù)棧優(yōu)越。假設獨立任務(wù)棧方法中每個(gè)堆??臻g為K,任務(wù)數為N,則獨立任務(wù)棧方式的堆??偪臻g為NK。在共用任務(wù)棧時(shí),考慮各任務(wù)互補的情況,TaskSTK變量不需要定義為NK長(cháng)度,可能定義為二分之一或者更小就可以了。

另外,這種方法不需要在任務(wù)切換時(shí)修改CPU的SP指針。

(2)工作棧和任務(wù)堆棧

上節共用任務(wù)棧算法的缺點(diǎn)是:任務(wù)切換時(shí)的堆棧內容交換算法復雜,占用時(shí)間長(cháng)。另外一個(gè)折中的方法是設計一個(gè)工作堆棧,用于給當前運行的任務(wù)使用;在任務(wù)切換時(shí),將工作棧內容換出得另外的存儲空間,該空間可以動(dòng)態(tài)申請,其大小按實(shí)際需要即可。

這種方法看起來(lái)和獨立任務(wù)棧的方法類(lèi)似,需要N+1塊存儲空間,其中一塊用于工作??臻g。和獨立任務(wù)堆棧相比,其區別有2點(diǎn):

①SP指針所指向的空間始終是同一塊存儲空間,即工作棧;

②每個(gè)任務(wù)棧的大小不需要按最大空間定義,可以動(dòng)態(tài)按實(shí)際大小從內存中分配空間。

對于8031這種處理器結構,由于堆棧指針只能指向其內部存儲器,大小十分有限。采取這種方法,可將工作棧設在內部RAM,將任務(wù)棧設在外部RAM,擴展了堆??臻g。

和上一種共用堆棧方法相比,這種方法的交換時(shí)間要短,其時(shí)間復雜度約為1.5倍最大任務(wù)棧長(cháng)度。

5 總結

獨立任務(wù)棧的方法適合于存儲器充足、任務(wù)切換頻繁、對任務(wù)切換時(shí)間要求較高的場(chǎng)合,一般主要用在16位或者32位微處理器平臺環(huán)境。值得注意的是,在某些微處理器中,雖然可使用的數據存儲器可以設計得較大,但堆棧所能使用的存儲器卻是有限的。比如8031系列存儲器,堆棧只能使用內部的128字節數據存儲器,即使系統中有64K字節的外部數據存儲器,任務(wù)棧的總空間也不能超過(guò)128字節。這種處理器使用共用任務(wù)棧結構的RTOS就更好一些。

由于共用任務(wù)棧系統需要較長(cháng)的任務(wù)切換時(shí)間,不適于任務(wù)切換頻繁的場(chǎng)合,在很多嵌入式系統中,長(cháng)時(shí)間只有幾個(gè)任務(wù)會(huì )處于運行狀態(tài),其它任務(wù)在特定的條件下才會(huì )運行。對于RTOS的使用者,也可以適當地劃分任務(wù),來(lái)減小任務(wù)切換的時(shí)間。

無(wú)論使用哪種方法,在存儲空間有限時(shí),任務(wù)棧的長(cháng)度應仔細計算。計算的根據是任務(wù)中的函數嵌套數、函數局部變量長(cháng)度。對于共用任務(wù)棧,還要考慮同時(shí)運行態(tài)和掛起態(tài)的最大任務(wù)數。一些編譯器可以生成堆棧溢出檢查代碼,在調試時(shí)可將該編譯開(kāi)關(guān)打開(kāi),以測試需要的實(shí)際堆棧長(cháng)度。



評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>