淺析μC/OS-ⅡAPI的設(shè)計思想及實現(xiàn)機制
void MyTask (void *pdata)
函數(shù)參數(shù)為一指針變量,指向?qū)?yīng)任務(wù)的OS_TCB結(jié)構(gòu)地址。本函數(shù)是有用的調(diào)試工具。
3. 消息和同步類API的設(shè)計思路和實現(xiàn)機制
μC/OS-Ⅱ中有三種方法實現(xiàn)消息通信和同步:信號量、郵箱和消息隊列。一個任務(wù)或者中斷服務(wù)子程序可以通過事件控制塊ECB(Event Control Blocks)來向另外的任務(wù)發(fā)信號。這里,所有的信號都被看成是事件(Event)。這也說明為什么上面把用于通訊的數(shù)據(jù)結(jié)構(gòu)叫做事件控制塊。一個任務(wù)還可以等待另一個任務(wù)或中斷服務(wù)子程序給它發(fā)送信號。這里要注意的是,只有任務(wù)可以等待事件發(fā)生,中斷服務(wù)子程序是不能這樣做的。對于處于等待狀態(tài)的任務(wù),還可以給它指定一個最長等待時間,以此來防止因為等待的事件沒有發(fā)生而無限期地等下去。多個任務(wù)可以同時等待同一個事件的發(fā)生。在這種情況下,當該事件發(fā)生后,所有等待該事件的任務(wù)中,優(yōu)先級最高的任務(wù)得到了該事件并進入就緒狀態(tài),準備執(zhí)行。上面講到的事件,可以是信號量、郵箱或者消息隊列等。當事件控制塊是一個信號量時,任務(wù)可以等待它,也可以給它發(fā)送消息。
μC/OS-II中的信號量由兩部分組成:一個是信號量的計數(shù)值,它是一個16位的無符號整數(shù)(0 到65,535之間);另一個是由等待該信號量的任務(wù)組成的等待任務(wù)表。用戶要在OS_CFG.H中將OS_SEM_EN開關(guān)量常數(shù)置成1,這樣μC/OS-II才能支持信號量。
郵箱是μC/OS-II中另一種通訊機制,它可以使一個任務(wù)或者中斷服務(wù)子程序向另一個任務(wù)發(fā)送一個指針型的變量。該指針指向一個包含了特定“消息”的數(shù)據(jù)結(jié)構(gòu)。為了在μC/OS-II中使用郵箱,必須將OS_CFG.H中的OS_MBOX_EN常數(shù)置為1。
消息隊列是μC/OS-II中另一種通訊機制,它可以使一個任務(wù)或者中斷服務(wù)子程序向另一個任務(wù)發(fā)送以指針方式定義的變量。因具體的應(yīng)用有所不同,每個指針指向的數(shù)據(jù)結(jié)構(gòu)變量也有所不同。為了使用μC/OS-II的消息隊列功能,需要在OS_CFG.H 文件中,將OS_Q_EN常數(shù)設(shè)置為1,并且通過常數(shù)OS_MAX_QS來決定μC/OS-II支持的最多消息隊列數(shù)。
μC/OS-Ⅱ提供一系列API函數(shù)供用戶調(diào)用,實現(xiàn)各個任務(wù)之間的通信和同步功能。下面以信號量為例說明各個API的實現(xiàn)。
μC/OS-II提供了5個對信號量進行操作的函數(shù)。它們是:OSSemCreate(),OSSemPend(),OSSemPost(),OSSemAccept()和OSSemQuery()函數(shù)。圖 F6.5說明了任務(wù)、中斷服務(wù)子程序和信號量之間的關(guān)系。圖中用鑰匙或者旗幟的符號來表示信號量:如果信號量用于對共享資源的訪問,那么信號量就用鑰匙符號。符號旁邊的數(shù)字N代表可用資源數(shù)。對于二值信號量,該值就是1;如果信號量用于表示某事件的發(fā)生,那么就用旗幟符號。這時的數(shù)字N代表事件已經(jīng)發(fā)生的次數(shù)。從圖 F6.5中可以看出OSSemPost()函數(shù)可以由任務(wù)或者中斷服務(wù)子程序調(diào)用,而OSSemPend()和OSSemQuery()函數(shù)只能有任務(wù)程序調(diào)用。
3.1建立一個信號量, OSSemCreate()
OS_EVENT *OSSemCreate (INT16U cnt)
函數(shù)參數(shù)傳遞的是要創(chuàng)建的信號量的初始值,在函數(shù)內(nèi)部對任務(wù)控制塊進行初始化。OSSemCreate()返回給調(diào)用函數(shù)一個指向任務(wù)控制塊的指針。以后對信號量的所有操作,如OSSemPend(), OSSemPost(), OSSemAccept()和OSSemQuery()都是通過該指針完成的。因此,這個指針實際上就是該信號量的句柄。如果系統(tǒng)中沒有可用的任務(wù)控制塊,OSSemCreate()將返回一個NULL指針。
值得注意的是,在μC/OS-II中,信號量一旦建立就不能刪除了,因此也就不可能將一個已分配的任務(wù)控制塊再放回到空閑ECB鏈表中。如果有任務(wù)正在等待某個信號量,或者某任務(wù)的運行依賴于某信號量的出現(xiàn)時,刪除該任務(wù)是很危險的。
3.2等待一個信號量, OSSemPend()
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
它首先檢查指針pevent所指的任務(wù)控制塊是否是由OSSemCreate()建立的。如果信號量當前是可用的(信號量的計數(shù)值大于0),將信號量的計數(shù)值減1,然后函數(shù)將“無錯”錯誤代碼返回給它的調(diào)用函數(shù)。顯然,如果正在等待信號量,這時的輸出正是我們所希望的,也是運行OSSemPend()函數(shù)最快的路徑。
3.3發(fā)送一個信號量, OSSemPost()
INT8U OSSemPost (OS_EVENT *pevent)
它首先檢查參數(shù)指針pevent指向的任務(wù)控制塊是否是OSSemCreate()函數(shù)建立的,接著檢查是否有任務(wù)在等待該信號量。如果該任務(wù)控制塊中的.OSEventGrp域不是0,說明有任務(wù)正在等待該信號量。這時,就要調(diào)用函數(shù)OSEventTaskRdy(),使一個任務(wù)進入就緒狀態(tài),把其中的最高優(yōu)先級任務(wù)從等待任務(wù)列表中刪除并使它進入就緒狀態(tài)。然后,調(diào)用OSSched()任務(wù)調(diào)度函數(shù)檢查該任務(wù)是否是系統(tǒng)中的最高優(yōu)先級的就緒任務(wù)。如果是,這時就要進行任務(wù)切換[當OSSemPost()函數(shù)是在任務(wù)中調(diào)用的],準備執(zhí)行該就緒任務(wù)。如果不是,OSSched()直接返回,調(diào)用OSSemPost()的任務(wù)得以繼續(xù)執(zhí)行。如果這時沒有任務(wù)在等待該信號量,該信號量的計數(shù)值就簡單地加1。
上面是由任務(wù)調(diào)用OSSemPost()時的情況。當中斷服務(wù)子程序調(diào)用該函數(shù)時,不會發(fā)生上面的任務(wù)切換。如果需要,任務(wù)切換要等到中斷嵌套的最外層中斷服務(wù)子程序調(diào)用OSIntExit()函數(shù)后才能進行。
3.4無等待地請求一個信號量, OSSemAccept()
INT16U OSSemAccept (OS_EVENT *pevent)
當一個任務(wù)請求一個信號量時,如果該信號量暫時無效,也可以讓該任務(wù)簡單地返回,而不是進入睡眠等待狀態(tài)。這種情況下的操作是由OSSemAccept()函數(shù)完成的。該函數(shù)在最開始也是檢查參數(shù)指針pevent指向的事件控制塊是否是由OSSemCreate()函數(shù)建立的,接著從該信號量的事件控制塊中取出當前計數(shù)值,并檢查該信號量是否有效(計數(shù)值是否為非0值)。如果有效,則將信號量的計數(shù)值減1,然后將信號量的原有計數(shù)值返回給調(diào)用函數(shù)。調(diào)用函數(shù)需要對該返回值進行檢查。如果該值是0,說明該信號量無效。如果該值大于0,說明該信號量有效,同時該值也暗示著該信號量當前可用的資源數(shù)。應(yīng)該注意的是,這些可用資源中,已經(jīng)被該調(diào)用函數(shù)自身占用了一個(該計數(shù)值已經(jīng)被減1)。中斷服務(wù)子程序要請求信號量時,只能用OSSemAccept()而不能用OSSemPend(),因為中斷服務(wù)子程序是不允許等待的。
評論