uCOS II 是一個(gè)源代碼公開的嵌入式實(shí)時(shí)操作系統(tǒng),以其穩(wěn)定可靠、高效、可移植性好,并且為占先式調(diào)度等特點(diǎn),被廣大工程技術(shù)人員使用。uCOS II 作為一種占先式的實(shí)時(shí)操作系統(tǒng),在不少方面有著可以與商業(yè)內(nèi)核相比的功能。但是uCOS II 不支持同優(yōu)先級(jí)任務(wù)的調(diào)度,而實(shí)際的應(yīng)用中,往往有些任務(wù)需要同優(yōu)先級(jí)進(jìn)行調(diào)度。如多點(diǎn)的溫度或氣壓數(shù)據(jù)采集,若理解為不同的優(yōu)先級(jí)任務(wù)去調(diào)度,不是一個(gè)好的邏輯設(shè)計(jì),并且可能需要更多地考慮如何去實(shí)現(xiàn)不同任務(wù)的調(diào)度。另外, 如果允許同優(yōu)先級(jí)任務(wù)調(diào)度,還可以解決優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題, 可以提升優(yōu)先級(jí)低但占有資源的任務(wù)至申請(qǐng)?jiān)撡Y源的高優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí), 直到低優(yōu)先級(jí)的任務(wù)釋放該資源, 恢復(fù)低優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),高優(yōu)先級(jí)的任務(wù)才占有該資源, 從而解決優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題。
1 uCOS II 的任務(wù)調(diào)度與OSTCBList結(jié)構(gòu)
當(dāng)兩個(gè)或兩個(gè)以上的任務(wù)有同樣優(yōu)先級(jí),內(nèi)核允許一個(gè)任務(wù)運(yùn)行事先確定的一段時(shí)間,叫做時(shí)間額度,然后切換給另一個(gè)任務(wù),也叫做時(shí)間片調(diào)度。內(nèi)核在滿足以下條件時(shí), 把CPU 控制權(quán)交給下一個(gè)任務(wù)就緒態(tài)的任務(wù):
當(dāng)前任務(wù)已無(wú)事可做;
當(dāng)前任務(wù)在時(shí)間片還沒(méi)有結(jié)束時(shí)已經(jīng)完成。
uCOS II 支持不同優(yōu)先級(jí)間的任務(wù)調(diào)度,通過(guò)任務(wù)各自對(duì)應(yīng)的優(yōu)先級(jí)狀態(tài),快速地由OSTCBPrioTbl[OSPrioHighRdy]指針的指向切換到期望的TCB 來(lái)完成調(diào)度。這種調(diào)度與uCOS II 的OSTCBList 數(shù)據(jù)結(jié)構(gòu)緊密相關(guān), 其數(shù)據(jù)結(jié)構(gòu)如圖1 所示。
這是一個(gè)雙向線性鏈表結(jié)構(gòu), 任務(wù)調(diào)度是從線性鏈表上的一個(gè)結(jié)點(diǎn)( 一個(gè)TCB 控制塊) 切換到從鏈表上的另一個(gè)結(jié)點(diǎn), 而不同的結(jié)點(diǎn)對(duì)應(yīng)著不同的優(yōu)先級(jí), 這樣來(lái)完成不同優(yōu)先級(jí)的任務(wù)調(diào)度。所以要想實(shí)現(xiàn)uCOS II 的同優(yōu)先級(jí)調(diào)度必須首先修改它的OSTCBList 數(shù)據(jù)結(jié)構(gòu),作如圖2 所示的修改。
任務(wù)1 、任務(wù)2 和任務(wù)3 為不同優(yōu)先級(jí)的任務(wù),任務(wù)1、任務(wù)4 和任務(wù)5 為同優(yōu)先級(jí)的任務(wù)。 當(dāng)沒(méi)有比任務(wù)1更高的優(yōu)先級(jí)任務(wù)時(shí), 每發(fā)生一次節(jié)拍中斷, 任務(wù)1 、任務(wù)4 和任務(wù)5 就依次在運(yùn)行態(tài)和就緒態(tài)切換。這里可以通過(guò)改變OSTCBPrioTbl[prio1]指向的任務(wù)TCB,也就是要修改OSSched()來(lái)實(shí)現(xiàn)。另外,很容易看出同優(yōu)先級(jí)任務(wù)間的數(shù)據(jù)結(jié)構(gòu)是一個(gè)循環(huán)鏈表結(jié)構(gòu), 所以TCB 也應(yīng)相應(yīng)的擴(kuò)展兩個(gè)指針域。新的數(shù)據(jù)結(jié)構(gòu)將使就緒任務(wù)的TCB 在一個(gè)循環(huán)鏈表和線性鏈表上不斷的切換。而對(duì)于等待任務(wù)的TCB , 也將在一個(gè)循環(huán)鏈表和線性鏈表上不斷的切換。這里需要增加一個(gè)全局變量的數(shù)組OSTCBWaitTbl[OS_LOWEST_PRIO]指向相對(duì)應(yīng)優(yōu)先級(jí)的等待任務(wù)的TCB ,以便控制就緒任務(wù)進(jìn)入等待任務(wù)或等待任務(wù)進(jìn)入就緒任務(wù)時(shí)鏈表上任務(wù)TCB 的插入和刪除。
2 對(duì)uCOS II的擴(kuò)展及相關(guān)函數(shù)的修改
首先擴(kuò)展TCB 的指針域, 定義新的TCB 為:
typedef struct os_tcb
{……
#if OS_TASK_PrioEqu_EN
struct OS_TCB *prioequNext;
struct OS_TCB*prioequPrev;
#endif
……}OS_TCB
增加的全局變量的數(shù)組定義為:
#if OS_TASK_PrioEqu_EN
OS_EXT OS_TCB OSTCBWaitTbl[OS_LOWEST_PRIO]
#endif
對(duì)OSTCBList 操作的每個(gè)相關(guān)函數(shù)都要修改, 主要修改以下方面的內(nèi)容。
① 對(duì)于鏈表數(shù)據(jù)結(jié)構(gòu)來(lái)說(shuō), 有以下幾點(diǎn)。
鏈表的初始化及頭結(jié)點(diǎn)。與OSTaskCreatExt()函數(shù)中的OSTCBInit()相關(guān),需要完成同任務(wù)TCB 構(gòu)成的循環(huán)鏈表及不同任務(wù)TCB 構(gòu)成的線性鏈表。
鏈表結(jié)點(diǎn)的插入和刪除,與任務(wù)的狀態(tài)變化有關(guān)。當(dāng)任務(wù)由就緒態(tài)進(jìn)入等待狀態(tài)或者由等待狀態(tài)進(jìn)入就緒態(tài)時(shí), 任務(wù)TCB 需要添加到相應(yīng)的鏈表中或從對(duì)應(yīng)的鏈表中刪除。這些函數(shù)與任務(wù)由就緒態(tài)進(jìn)入等待狀態(tài)或者由等待狀態(tài)進(jìn)入就緒態(tài)相關(guān),有OSEventTaskWait(),OSEventTo(),OSTaskRdy(),OSTaskDel()以及OSTaskChangePrio()。
② 對(duì)同優(yōu)先級(jí)任務(wù)調(diào)度時(shí),由OSTCBPrioTbl[prio]指針的移動(dòng)實(shí)現(xiàn)TCB 的切換,這里是對(duì)循環(huán)鏈表的操作,可以修改OSSched()實(shí)現(xiàn)。
③ 由時(shí)間延時(shí)滿引起的超時(shí),使等待的任務(wù)進(jìn)入就緒態(tài), 這一方面是對(duì)鏈表結(jié)點(diǎn)的插入和刪除, 另外還是對(duì)任務(wù)延時(shí)的控制,需要修改OSTimeDly()和OSTimeTick()。
對(duì)這幾個(gè)函數(shù)修改需要注意的是, 當(dāng)要對(duì)某個(gè)優(yōu)先級(jí)的循環(huán)鏈表插入結(jié)點(diǎn)前或刪除結(jié)點(diǎn)后對(duì)應(yīng)的TCB 為空時(shí), 需要對(duì)就緒列表或等待列表相應(yīng)的位置位或清零。
3 解決優(yōu)先級(jí)反轉(zhuǎn)的方法
任務(wù)對(duì)共享資源的占有和釋放是通過(guò)對(duì)信號(hào)量的操作來(lái)實(shí)現(xiàn)的。
voidTask()
{……
OSSemPend();//等待信號(hào)量
共享資源;
OSSemPost();//釋放信號(hào)量
……
}
那么,如何提升優(yōu)先級(jí)低但占有資源的任務(wù)至申請(qǐng)?jiān)撡Y源的高優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),直到低優(yōu)先級(jí)的任務(wù)釋放該資源,恢復(fù)低優(yōu)先級(jí)任務(wù)的優(yōu)先級(jí),高優(yōu)先級(jí)的任務(wù)才占有該資源。首先這里需要改變?nèi)蝿?wù)優(yōu)先級(jí), 改變的優(yōu)先級(jí)分別是提升優(yōu)先級(jí)到高優(yōu)先級(jí)和恢復(fù)任務(wù)到低優(yōu)先級(jí)。顯然,需要存儲(chǔ)將要改變結(jié)果的優(yōu)先級(jí)數(shù),可以通過(guò)擴(kuò)展OS_Event 來(lái)實(shí)現(xiàn)。
typestruct{
void *OSEventPtr;
……
int8u prio;}OS_Event
然后分別修改OSSemPend()和OSSemPost()。
void OSSemPend()
{……
if(pevent->OSEventCnt>0)
{pevebt->OSEventCnt--;
#if OS_TASK_PrioEqu_EN
if(pevent->OSEventCnt<1)
{pevent->prio=OSTCBCur->prio;}
//當(dāng)資源恰好分配完時(shí),記下當(dāng)前任務(wù)優(yōu)先級(jí)
#endif
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;}
#if OS_TASK_PrioEqu_EN
elseif
{OSTaskChangePrio(pevent->prio,OSTCBCUR->prio);}
//當(dāng)任務(wù)等待分配資源時(shí)提升占有資源任務(wù)的優(yōu)先級(jí)
#endif
……
}
int8u OSSemPost()
{……
if(pevent->OSEventCnt<65535)
{pevent->OSEventCnt++;
#if OS_TASK_PrioEqu_EN
if(pevent->OSEventCnt<2)
OSTaskChangePrio(OSTCBCur->prio,pevent->prio);}
//當(dāng)任務(wù)釋放臨界資源時(shí)恢復(fù)一個(gè)任務(wù)的優(yōu)先級(jí)
#endif
OS_EXIT_CRITICAL();
return(OS_M_ERR);
……}
通過(guò)對(duì)這幾個(gè)函數(shù)的修改, 實(shí)現(xiàn)了優(yōu)先級(jí)的繼承,解決了優(yōu)先級(jí)的反轉(zhuǎn)。問(wèn)題的關(guān)鍵是對(duì)同優(yōu)先級(jí)調(diào)度的支持和對(duì)資源分配的申請(qǐng)與釋放過(guò)程的約定。
結(jié)語(yǔ)
uCOS II 是一個(gè)良好的實(shí)時(shí)操作系統(tǒng)。隨著更多的人使用和不斷的改進(jìn), 將會(huì)有更好的應(yīng)用前景。