淺析μC/OS-ⅡAPI的設(shè)計(jì)思想及實(shí)現(xiàn)機(jī)制
任何一個(gè)操作系統(tǒng)都會(huì)提供大量的API供程序員使用,μC/OS-Ⅱ也不例外。由于μC/OS-Ⅱ面向的是嵌入式開(kāi)發(fā),并不要求大而全,所以內(nèi)核提供的API也就大多和多任務(wù)息息相關(guān)。本文通過(guò)分析μC/OS-Ⅱ中提供的API來(lái)引出μC/OS-Ⅱ中API的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制。
API全稱ApplicaTION Programming Interface,中文是應(yīng)用程序編程接口的意思。API是操作系統(tǒng)提供給用戶的一組函數(shù),供用戶在編寫(xiě)應(yīng)用程序時(shí)調(diào)用,可以完成應(yīng)用程序?qū)Σ僮飨到y(tǒng)的各種調(diào)用,包括進(jìn)程調(diào)度、存儲(chǔ)管理、圖形設(shè)備接口及文件管理等。μC/OS-Ⅱ作為一個(gè)嵌入式操作系統(tǒng),相對(duì)于其他操作系統(tǒng),有很多自己的特色,在設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制上都和一般操作系統(tǒng)有很大的不同。
1. 簡(jiǎn)介
任何一個(gè)操作系統(tǒng)都會(huì)提供大量的API供程序員使用,μC/OS-Ⅱ也不例外。由于μC/OS-Ⅱ面向的是嵌入式開(kāi)發(fā),并不要求大而全,所以內(nèi)核提供的API也就大多和多任務(wù)息息相關(guān)。μC/OS-Ⅱ的API主要分以下幾類:(1)任務(wù)類、(2)消息類、(3)同步類、(4)時(shí)間類、(5)臨界區(qū)與事件類等。下面分別從這幾類API分析各自的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制。
2. 任務(wù)類API的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制
μC/OS-Ⅱ可以管理多達(dá)64個(gè)任務(wù),并從中保留了四個(gè)最高優(yōu)先級(jí)和四個(gè)最低優(yōu)先級(jí)的任務(wù)供自己使用,所以用戶可以使用的只有56個(gè)任務(wù)。任務(wù)類API包括如何在用戶的應(yīng)用程序中建立任務(wù)、刪除任務(wù)、改變?nèi)蝿?wù)的優(yōu)先級(jí)、掛起和恢復(fù)任務(wù),以及獲得有關(guān)任務(wù)的信息等。
2.1 建立任務(wù)API
想讓μC/OS-Ⅱ管理用戶的任務(wù),用戶必須要先建立任務(wù)。用戶可以通過(guò)傳遞任務(wù)地址和其它參數(shù)到以下兩個(gè)函數(shù)之一來(lái)建立任務(wù):OSTaskCreate() 或 OSTaskCreateExt()。
OSTaskCreate()與μC/OS是向下兼容的,OSTaskCreateExt()是OSTaskCreate()的擴(kuò)展版本,提供了一些附加的功能。用兩個(gè)函數(shù)中的任何一個(gè)都可以建立任務(wù)。任務(wù)可以在多任務(wù)調(diào)度開(kāi)始前建立,也可以在其它任務(wù)的執(zhí)行過(guò)程中被建立。在開(kāi)始多任務(wù)調(diào)度(即調(diào)用OSStart())前,用戶必須建立至少一個(gè)任務(wù)。任務(wù)不能由中斷服務(wù)程序(ISR)來(lái)建立。
OSTaskCreate()的函數(shù)定義如下。從中可以知道,OSTaskCreate()需要四個(gè)參數(shù):task是任務(wù)代碼的指針,pdata是當(dāng)任務(wù)開(kāi)始執(zhí)行時(shí)傳遞給任務(wù)的參數(shù)的指針,ptos是分配給任務(wù)的堆棧的棧頂指針,prio是分配給任務(wù)的優(yōu)先級(jí)。
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
用OSTaskCreateExt()函數(shù)來(lái)建立任務(wù)會(huì)更加靈活,但會(huì)增加一些額外的開(kāi)銷。
OSTaskCreateExt()需要九個(gè)參數(shù)!前四個(gè)參數(shù)(task,pdata,ptos和prio)與OSTaskCreate()的四個(gè)參數(shù)完全相同,連先后順序都一樣。這樣做的目的是為了使用戶能夠更容易地將用戶的程序從OSTaskCreate()移植到OSTaskCreateExt()上去。函數(shù)的定義如下:
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)
2.2 刪除任務(wù)API
有時(shí)候刪除任務(wù)是很有必要的。刪除任務(wù),是說(shuō)任務(wù)將返回并處于休眠狀態(tài),并不是說(shuō)任務(wù)的代碼被刪除了,只是任務(wù)的代碼不再被μC/OS-Ⅱ調(diào)用。通過(guò)調(diào)用OSTaskDel()就可以完成刪除任務(wù)的功能。OSTaskDel()一開(kāi)始應(yīng)確保用戶所要?jiǎng)h除的任務(wù)并非是空閑任務(wù),因?yàn)閯h除空閑任務(wù)是不允許的。不過(guò),用戶可以刪除statistic任務(wù)。接著,OSTaskDel()還應(yīng)確保用戶不是在ISR例程中去試圖刪除一個(gè)任務(wù),因?yàn)檫@也是不被允許的。調(diào)用此函數(shù)的任務(wù)可以通過(guò)指定OS_PRIO_SELF參數(shù)來(lái)刪除自己。接下來(lái)OSTaskDel()會(huì)保證被刪除的任務(wù)是確實(shí)存在的。該函數(shù)的入口參數(shù)很簡(jiǎn)單,只需要知道要?jiǎng)h除任務(wù)的優(yōu)先級(jí)即可。
INT8U OSTaskDel (INT8U prio)
2.3 改變?nèi)蝿?wù)優(yōu)先級(jí)API
在用戶建立任務(wù)的時(shí)候會(huì)分配給任務(wù)一個(gè)優(yōu)先級(jí)。在程序運(yùn)行期間,用戶可以通過(guò)調(diào)用OSTaskChangePrio()來(lái)改變?nèi)蝿?wù)的優(yōu)先級(jí)。換句話說(shuō),就是μC/OS-Ⅱ允許用戶動(dòng)態(tài)的改變?nèi)蝿?wù)的優(yōu)先級(jí)。函數(shù)定義如下:
INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
用戶不能改變空閑任務(wù)的優(yōu)先級(jí),但用戶可以改變調(diào)用本函數(shù)的任務(wù)或者其它任務(wù)的優(yōu)先級(jí)。為了改變調(diào)用本函數(shù)的任務(wù)的優(yōu)先級(jí),用戶可以指定該任務(wù)當(dāng)前的優(yōu)先級(jí)或OS_PRIO_SELF,OSTaskChangePrio()會(huì)決定該任務(wù)的優(yōu)先級(jí)。用戶還必須指定任務(wù)的新(即想要的)優(yōu)先級(jí)。因?yàn)?mu;C/OS-Ⅱ不允許多個(gè)任務(wù)具有相同的優(yōu)先級(jí),所以O(shè)STaskChangePrio()需要檢驗(yàn)新優(yōu)先級(jí)是否是合法的(即不存在具有新優(yōu)先級(jí)的任務(wù))。如果新優(yōu)先級(jí)是合法的,μC/OS-Ⅱ通過(guò)將某些東西儲(chǔ)存到OSTCBPrioTbl[newprio]中保留這個(gè)優(yōu)先級(jí)。如此就使得OSTaskChangePrio()可以重新允許中斷,因?yàn)榇藭r(shí)其它任務(wù)已經(jīng)不可能建立擁有該優(yōu)先級(jí)的任務(wù),也不能通過(guò)指定相同的新優(yōu)先級(jí)來(lái)調(diào)用OSTaskChangePrio()。接下來(lái)OSTaskChangePrio()可以預(yù)先計(jì)算新優(yōu)先級(jí)任務(wù)的OS_TCB中的某些值。而這些值用來(lái)將任務(wù)放入就緒表或從該表中移除。
2.4 掛起任務(wù)和恢復(fù)任務(wù)API
有時(shí)候?qū)⑷蝿?wù)掛起是很有用的。掛起任務(wù)可通過(guò)調(diào)用OSTaskSuspend()函數(shù)來(lái)完成。被掛起的任務(wù)只能通過(guò)調(diào)用OSTaskResume()函數(shù)來(lái)恢復(fù)。任務(wù)掛起是一個(gè)附加功能。也就是說(shuō),如果任務(wù)在被掛起的同時(shí)也在等待延時(shí)的期滿,那么,掛起操作需要被取消,而任務(wù)繼續(xù)等待延時(shí)期滿,并轉(zhuǎn)入就緒狀態(tài)。任務(wù)可以掛起自己或者其它任務(wù)。
OSTaskSuspend()函數(shù)的函數(shù)定義如下:
INT8U OSTaskSuspend (INT8U prio)
恢復(fù)任務(wù)OSTaskResume()函數(shù)定義為:
INT8U OSTaskResume (INT8U prio)
被掛起的任務(wù)只有通過(guò)調(diào)用OSTaskResume()才能恢復(fù)。因?yàn)镺STaskSuspend()不能掛起空閑任務(wù),所以必須得確認(rèn)用戶的應(yīng)用程序不是在恢復(fù)空閑任務(wù)。注意,這個(gè)測(cè)試也可以確保用戶不是在恢復(fù)優(yōu)先級(jí)為OS_PRIO_SELF的任務(wù)(OS_PRIO_SELF被定義為0xFF,它總是比OS_LOWEST_PRIO大)。
2.5 獲得任務(wù)信息API
用戶的應(yīng)用程序可以通過(guò)調(diào)用OSTaskQuery()來(lái)獲得自身或其它應(yīng)用任務(wù)的信息。實(shí)際上,OSTaskQuery()獲得的是對(duì)應(yīng)任務(wù)的OS_TCB中內(nèi)容的拷貝。用戶能訪問(wèn)的OS_TCB的數(shù)據(jù)域的多少?zèng)Q定于用戶的應(yīng)用程序的配置(參看OS_CFG.H)。由于μC/OS-Ⅱ是可裁剪的,它只包括那些用戶的應(yīng)用程序所要求的屬性和功能。[!--empirenews.page--]
void MyTask (void *pdata)
函數(shù)參數(shù)為一指針變量,指向?qū)?yīng)任務(wù)的OS_TCB結(jié)構(gòu)地址。本函數(shù)是有用的調(diào)試工具。
3. 消息和同步類API的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制
μC/OS-Ⅱ中有三種方法實(shí)現(xiàn)消息通信和同步:信號(hào)量、郵箱和消息隊(duì)列。一個(gè)任務(wù)或者中斷服務(wù)子程序可以通過(guò)事件控制塊ECB(Event Control Blocks)來(lái)向另外的任務(wù)發(fā)信號(hào)。這里,所有的信號(hào)都被看成是事件(Event)。這也說(shuō)明為什么上面把用于通訊的數(shù)據(jù)結(jié)構(gòu)叫做事件控制塊。一個(gè)任務(wù)還可以等待另一個(gè)任務(wù)或中斷服務(wù)子程序給它發(fā)送信號(hào)。這里要注意的是,只有任務(wù)可以等待事件發(fā)生,中斷服務(wù)子程序是不能這樣做的。對(duì)于處于等待狀態(tài)的任務(wù),還可以給它指定一個(gè)最長(zhǎng)等待時(shí)間,以此來(lái)防止因?yàn)榈却氖录](méi)有發(fā)生而無(wú)限期地等下去。多個(gè)任務(wù)可以同時(shí)等待同一個(gè)事件的發(fā)生。在這種情況下,當(dāng)該事件發(fā)生后,所有等待該事件的任務(wù)中,優(yōu)先級(jí)最高的任務(wù)得到了該事件并進(jìn)入就緒狀態(tài),準(zhǔn)備執(zhí)行。上面講到的事件,可以是信號(hào)量、郵箱或者消息隊(duì)列等。當(dāng)事件控制塊是一個(gè)信號(hào)量時(shí),任務(wù)可以等待它,也可以給它發(fā)送消息。
μC/OS-II中的信號(hào)量由兩部分組成:一個(gè)是信號(hào)量的計(jì)數(shù)值,它是一個(gè)16位的無(wú)符號(hào)整數(shù)(0 到65,535之間);另一個(gè)是由等待該信號(hào)量的任務(wù)組成的等待任務(wù)表。用戶要在OS_CFG.H中將OS_SEM_EN開(kāi)關(guān)量常數(shù)置成1,這樣μC/OS-II才能支持信號(hào)量。
郵箱是μC/OS-II中另一種通訊機(jī)制,它可以使一個(gè)任務(wù)或者中斷服務(wù)子程序向另一個(gè)任務(wù)發(fā)送一個(gè)指針型的變量。該指針指向一個(gè)包含了特定“消息”的數(shù)據(jù)結(jié)構(gòu)。為了在μC/OS-II中使用郵箱,必須將OS_CFG.H中的OS_MBOX_EN常數(shù)置為1。
消息隊(duì)列是μC/OS-II中另一種通訊機(jī)制,它可以使一個(gè)任務(wù)或者中斷服務(wù)子程序向另一個(gè)任務(wù)發(fā)送以指針?lè)绞蕉x的變量。因具體的應(yīng)用有所不同,每個(gè)指針指向的數(shù)據(jù)結(jié)構(gòu)變量也有所不同。為了使用μC/OS-II的消息隊(duì)列功能,需要在OS_CFG.H 文件中,將OS_Q_EN常數(shù)設(shè)置為1,并且通過(guò)常數(shù)OS_MAX_QS來(lái)決定μC/OS-II支持的最多消息隊(duì)列數(shù)。
μC/OS-Ⅱ提供一系列API函數(shù)供用戶調(diào)用,實(shí)現(xiàn)各個(gè)任務(wù)之間的通信和同步功能。下面以信號(hào)量為例說(shuō)明各個(gè)API的實(shí)現(xiàn)。
μC/OS-II提供了5個(gè)對(duì)信號(hào)量進(jìn)行操作的函數(shù)。它們是:OSSemCreate(),OSSemPend(),OSSemPost(),OSSemAccept()和OSSemQuery()函數(shù)。圖 F6.5說(shuō)明了任務(wù)、中斷服務(wù)子程序和信號(hào)量之間的關(guān)系。圖中用鑰匙或者旗幟的符號(hào)來(lái)表示信號(hào)量:如果信號(hào)量用于對(duì)共享資源的訪問(wèn),那么信號(hào)量就用鑰匙符號(hào)。符號(hào)旁邊的數(shù)字N代表可用資源數(shù)。對(duì)于二值信號(hào)量,該值就是1;如果信號(hào)量用于表示某事件的發(fā)生,那么就用旗幟符號(hào)。這時(shí)的數(shù)字N代表事件已經(jīng)發(fā)生的次數(shù)。從圖 F6.5中可以看出OSSemPost()函數(shù)可以由任務(wù)或者中斷服務(wù)子程序調(diào)用,而OSSemPend()和OSSemQuery()函數(shù)只能有任務(wù)程序調(diào)用。
3.1建立一個(gè)信號(hào)量, OSSemCreate()
OS_EVENT *OSSemCreate (INT16U cnt)
函數(shù)參數(shù)傳遞的是要?jiǎng)?chuàng)建的信號(hào)量的初始值,在函數(shù)內(nèi)部對(duì)任務(wù)控制塊進(jìn)行初始化。OSSemCreate()返回給調(diào)用函數(shù)一個(gè)指向任務(wù)控制塊的指針。以后對(duì)信號(hào)量的所有操作,如OSSemPend(), OSSemPost(), OSSemAccept()和OSSemQuery()都是通過(guò)該指針完成的。因此,這個(gè)指針實(shí)際上就是該信號(hào)量的句柄。如果系統(tǒng)中沒(méi)有可用的任務(wù)控制塊,OSSemCreate()將返回一個(gè)NULL指針。
值得注意的是,在μC/OS-II中,信號(hào)量一旦建立就不能刪除了,因此也就不可能將一個(gè)已分配的任務(wù)控制塊再放回到空閑ECB鏈表中。如果有任務(wù)正在等待某個(gè)信號(hào)量,或者某任務(wù)的運(yùn)行依賴于某信號(hào)量的出現(xiàn)時(shí),刪除該任務(wù)是很危險(xiǎn)的。
3.2等待一個(gè)信號(hào)量, OSSemPend()
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
它首先檢查指針pevent所指的任務(wù)控制塊是否是由OSSemCreate()建立的。如果信號(hào)量當(dāng)前是可用的(信號(hào)量的計(jì)數(shù)值大于0),將信號(hào)量的計(jì)數(shù)值減1,然后函數(shù)將“無(wú)錯(cuò)”錯(cuò)誤代碼返回給它的調(diào)用函數(shù)。顯然,如果正在等待信號(hào)量,這時(shí)的輸出正是我們所希望的,也是運(yùn)行OSSemPend()函數(shù)最快的路徑。
3.3發(fā)送一個(gè)信號(hào)量, OSSemPost()
INT8U OSSemPost (OS_EVENT *pevent)
它首先檢查參數(shù)指針pevent指向的任務(wù)控制塊是否是OSSemCreate()函數(shù)建立的,接著檢查是否有任務(wù)在等待該信號(hào)量。如果該任務(wù)控制塊中的.OSEventGrp域不是0,說(shuō)明有任務(wù)正在等待該信號(hào)量。這時(shí),就要調(diào)用函數(shù)OSEventTaskRdy(),使一個(gè)任務(wù)進(jìn)入就緒狀態(tài),把其中的最高優(yōu)先級(jí)任務(wù)從等待任務(wù)列表中刪除并使它進(jìn)入就緒狀態(tài)。然后,調(diào)用OSSched()任務(wù)調(diào)度函數(shù)檢查該任務(wù)是否是系統(tǒng)中的最高優(yōu)先級(jí)的就緒任務(wù)。如果是,這時(shí)就要進(jìn)行任務(wù)切換[當(dāng)OSSemPost()函數(shù)是在任務(wù)中調(diào)用的],準(zhǔn)備執(zhí)行該就緒任務(wù)。如果不是,OSSched()直接返回,調(diào)用OSSemPost()的任務(wù)得以繼續(xù)執(zhí)行。如果這時(shí)沒(méi)有任務(wù)在等待該信號(hào)量,該信號(hào)量的計(jì)數(shù)值就簡(jiǎn)單地加1。
上面是由任務(wù)調(diào)用OSSemPost()時(shí)的情況。當(dāng)中斷服務(wù)子程序調(diào)用該函數(shù)時(shí),不會(huì)發(fā)生上面的任務(wù)切換。如果需要,任務(wù)切換要等到中斷嵌套的最外層中斷服務(wù)子程序調(diào)用OSIntExit()函數(shù)后才能進(jìn)行。
3.4無(wú)等待地請(qǐng)求一個(gè)信號(hào)量, OSSemAccept()
INT16U OSSemAccept (OS_EVENT *pevent)
當(dāng)一個(gè)任務(wù)請(qǐng)求一個(gè)信號(hào)量時(shí),如果該信號(hào)量暫時(shí)無(wú)效,也可以讓該任務(wù)簡(jiǎn)單地返回,而不是進(jìn)入睡眠等待狀態(tài)。這種情況下的操作是由OSSemAccept()函數(shù)完成的。該函數(shù)在最開(kāi)始也是檢查參數(shù)指針pevent指向的事件控制塊是否是由OSSemCreate()函數(shù)建立的,接著從該信號(hào)量的事件控制塊中取出當(dāng)前計(jì)數(shù)值,并檢查該信號(hào)量是否有效(計(jì)數(shù)值是否為非0值)。如果有效,則將信號(hào)量的計(jì)數(shù)值減1,然后將信號(hào)量的原有計(jì)數(shù)值返回給調(diào)用函數(shù)。調(diào)用函數(shù)需要對(duì)該返回值進(jìn)行檢查。如果該值是0,說(shuō)明該信號(hào)量無(wú)效。如果該值大于0,說(shuō)明該信號(hào)量有效,同時(shí)該值也暗示著該信號(hào)量當(dāng)前可用的資源數(shù)。應(yīng)該注意的是,這些可用資源中,已經(jīng)被該調(diào)用函數(shù)自身占用了一個(gè)(該計(jì)數(shù)值已經(jīng)被減1)。中斷服務(wù)子程序要請(qǐng)求信號(hào)量時(shí),只能用OSSemAccept()而不能用OSSemPend(),因?yàn)橹袛喾?wù)子程序是不允許等待的。[!--empirenews.page--]
3.5查詢一個(gè)信號(hào)量的當(dāng)前狀態(tài), OSSemQuery()
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)
在應(yīng)用程序中,用戶隨時(shí)可以調(diào)用函數(shù)OSSemQuery()來(lái)查詢一個(gè)信號(hào)量的當(dāng)前狀態(tài)。該函數(shù)有兩個(gè)參數(shù):一個(gè)是指向信號(hào)量對(duì)應(yīng)事件控制塊的指針pevent。該指針是在生產(chǎn)信號(hào)量時(shí),由OSSemCreate()函數(shù)返回的;另一個(gè)是指向用于記錄信號(hào)量信息的數(shù)據(jù)結(jié)構(gòu)OS_SEM_DATA(見(jiàn)uCOS_II.H)的指針pdata。因此,調(diào)用該函數(shù)前,用戶必須先定義該結(jié)構(gòu)變量,用于存儲(chǔ)信號(hào)量的有關(guān)信息。在這里,之所以使用一個(gè)新的數(shù)據(jù)結(jié)構(gòu)的原因在于,調(diào)用函數(shù)應(yīng)該只關(guān)心那些和特定信號(hào)量有關(guān)的信息,而不是象OS_EVENT數(shù)據(jù)結(jié)構(gòu)包含的很全面的信息。該數(shù)據(jù)結(jié)構(gòu)只包含信號(hào)量計(jì)數(shù)值.OSCnt和等待任務(wù)列表.OSEventTbl[]、.OSEventGrp,而OS_EVENT中還包含了另外的兩個(gè)域.OSEventType和.OSEventPtr。
4. 時(shí)間類API的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制
μC/OS-Ⅱ(其它內(nèi)核也一樣)要求用戶提供定時(shí)中斷來(lái)實(shí)現(xiàn)延時(shí)與超時(shí)控制等功能。這個(gè)定時(shí)中斷叫做時(shí)鐘節(jié)拍,它應(yīng)該每秒發(fā)生10至100次。時(shí)鐘節(jié)拍的實(shí)際頻率是由用戶的應(yīng)用程序決定的。時(shí)鐘節(jié)拍的頻率越高,系統(tǒng)的負(fù)荷就越重。
下面主要講述五個(gè)與時(shí)鐘節(jié)拍有關(guān)的API函數(shù)。
4.1 任務(wù)延時(shí)函數(shù),OSTimeDly()
void OSTimeDly (INT16U ticks)
這應(yīng)該程序員們調(diào)用最多的一個(gè)函數(shù)了,這個(gè)函數(shù)完成功能很簡(jiǎn)單,就是先掛起當(dāng)起當(dāng)前任務(wù),然后進(jìn)行任務(wù)切換,在指定的時(shí)間到來(lái)之后,將當(dāng)前任務(wù)恢復(fù)為就緒狀態(tài),但是并不一定運(yùn)行,如果恢復(fù)后是優(yōu)先級(jí)最高就緒任務(wù)的話,那么運(yùn)行之。簡(jiǎn)單點(diǎn)說(shuō),就是可以任務(wù)延時(shí)一定時(shí)間后再次執(zhí)行它,或者說(shuō),暫時(shí)放棄CPU的使用權(quán)。一個(gè)任務(wù)可以不顯式的調(diào)用這些可以導(dǎo)致放棄CPU使用權(quán)的API,但那樣多任務(wù)性能會(huì)大大降低,因?yàn)榇藭r(shí)僅僅依靠時(shí)鐘機(jī)制在進(jìn)行任務(wù)切換。一個(gè)好的任務(wù)應(yīng)該在完成一些操作主動(dòng)放棄使用權(quán)。
4.2 按時(shí)分秒延時(shí)函數(shù) OSTimeDlyHMSM()
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)
OSTimeDly()雖然是一個(gè)非常有用的函數(shù),但用戶的應(yīng)用程序需要知道延時(shí)時(shí)間對(duì)應(yīng)的時(shí)鐘節(jié)拍的數(shù)目。增加了OSTimeDlyHMSM()函數(shù)后,用戶就可以按小時(shí)(H)、分(M)、秒(S)和毫秒(m)來(lái)定義時(shí)間了,這樣會(huì)顯得更自然些。與OSTimeDly()一樣,調(diào)用OSTimeDlyHMSM()函數(shù)也會(huì)使μC/OS-Ⅱ進(jìn)行一次任務(wù)調(diào)度,并且執(zhí)行下一個(gè)優(yōu)先級(jí)最高的就緒態(tài)任務(wù)。任務(wù)調(diào)用OSTimeDlyHMSM()后,一旦規(guī)定的時(shí)間期滿或者有其它的任務(wù)通過(guò)調(diào)用OSTimeDlyResume()取消了延時(shí),它就會(huì)馬上處于就緒態(tài)。同樣,只有當(dāng)該任務(wù)在所有就緒態(tài)任務(wù)中具有最高的優(yōu)先級(jí)時(shí),它才會(huì)立即運(yùn)行。
4.3 讓處在延時(shí)期的任務(wù)結(jié)束延時(shí),OSTimeDlyResume()
INT8U OSTimeDlyResume (INT8U prio)
μC/OS-Ⅱ允許用戶結(jié)束延時(shí)正處于延時(shí)期的任務(wù)。延時(shí)的任務(wù)可以不等待延時(shí)期滿,而是通過(guò)其它任務(wù)取消延時(shí)來(lái)使自己處于就緒態(tài)。這可以通過(guò)調(diào)用OSTimeDlyResume()和指定要恢復(fù)的任務(wù)的優(yōu)先級(jí)來(lái)完成。實(shí)際上,OSTimeDlyResume()也可以喚醒正在等待事件的任務(wù),雖然這一點(diǎn)并沒(méi)有提到過(guò)。在這種情況下,等待事件發(fā)生的任務(wù)會(huì)考慮是否終止等待事件。
4.4 系統(tǒng)時(shí)間,OSTimeGet()和OSTimeSet()
INT32U OSTimeGet (void)
void OSTimeSet (INT32U ticks)
用戶可以通過(guò)調(diào)用OSTimeGet()來(lái)獲得該計(jì)數(shù)器的當(dāng)前值。也可以通過(guò)調(diào)用OSTimeSet()來(lái)改變?cè)撚?jì)數(shù)器的值。注意,在訪問(wèn)OSTime的時(shí)候中斷是關(guān)掉的。這是因?yàn)樵诖蠖鄶?shù)8位處理器上增加和拷貝一個(gè)32位的數(shù)都需要數(shù)條指令,這些指令一般都需要一次執(zhí)行完畢,而不能被中斷等因素打斷。
5. 臨界區(qū)類API的設(shè)計(jì)思路和實(shí)現(xiàn)機(jī)制
和其它內(nèi)核一樣,μC/OS-Ⅱ?yàn)榱颂幚砼R界段代碼需要關(guān)中斷,處理完畢后再開(kāi)中斷。這使得μC/OS-Ⅱ能夠避免同時(shí)有其它任務(wù)或中斷服務(wù)進(jìn)入臨界段代碼。
μC/OS-Ⅱ定義兩個(gè)宏(macros)來(lái)關(guān)中斷和開(kāi)中斷,以便避開(kāi)不同C編譯器廠商選擇不同的方法來(lái)處理關(guān)中斷和開(kāi)中斷。μC/OS-Ⅱ中的這兩個(gè)宏調(diào)用分別是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。因?yàn)檫@兩個(gè)宏的定義取決于所用的微處理器,故在文件OS_CPU.H中可以找到相應(yīng)宏定義。每種微處理器都有自己的OS_CPU.H文件。
5.1 OS_ENTER_CRITICAL宏
很多人都以為它是個(gè)函數(shù),其實(shí)不然,仔細(xì)分析一下OS_CPU.H文件,它和下面馬上要談到的OS_EXIT_CRITICAL都是宏。他們都是涉及特定CPU的實(shí)現(xiàn)。一般都被替換為一條或者幾條嵌入式匯編代碼。由于系統(tǒng)希望向上層程序員隱藏內(nèi)部實(shí)現(xiàn),故而一般都宣稱執(zhí)行此條指令后系統(tǒng)進(jìn)入臨界區(qū)。其實(shí),它就是關(guān)個(gè)中斷而已。這樣,只要任務(wù)不主動(dòng)放棄CPU使用權(quán),別的任務(wù)就沒(méi)有占用CPU的機(jī)會(huì)了,相對(duì)這個(gè)任務(wù)而言,它就是獨(dú)占了。所以說(shuō)進(jìn)入臨界區(qū)了。這個(gè)宏能少用還是少用,因?yàn)樗鼤?huì)破壞系統(tǒng)的一些服務(wù),尤其是時(shí)間服務(wù)。并使系統(tǒng)對(duì)外界響應(yīng)性能降低。
5.2 OS_EXIT_CRITICAL宏
這個(gè)是和上面介紹的宏配套使用另一個(gè)宏,它在系統(tǒng)手冊(cè)里的說(shuō)明是退出臨界區(qū)。其實(shí)它就是重新開(kāi)中斷。需要注意的是,它必須和上面的宏成對(duì)出現(xiàn),否則會(huì)帶來(lái)意想不到的后果。最壞的情況下,系統(tǒng)會(huì)崩潰。我們推薦程序員們盡量少使用這兩個(gè)宏調(diào)用,因?yàn)樗麄兊拇_會(huì)破壞系統(tǒng)的多任務(wù)性能。
6. 結(jié)束語(yǔ)
通過(guò)對(duì)μC/OS-II中這幾類API的簡(jiǎn)單分析,我們可以看出μC/OS-II作為一個(gè)多任務(wù)、搶占式嵌入式操作系統(tǒng),其API都是為多任務(wù)及其多任務(wù)之間的調(diào)度和通信的實(shí)現(xiàn)來(lái)設(shè)計(jì)的。μC/OS-II提供的API簡(jiǎn)潔而靈活,使用戶在設(shè)計(jì)實(shí)際應(yīng)用時(shí)能夠很方便的利用系統(tǒng)提供的各種調(diào)用,充分發(fā)揮μC/OS-II實(shí)時(shí)、高效的優(yōu)點(diǎn)。