μC/OS-II的任務之間的通訊與同步
值得注意的是,在μC/OS-II中,信號量一旦建立就不能刪除了,因此也就不可能將一個已分配的任務控制塊再放回到空閑ECB鏈表中。如果有任務正在等待某個信號量,或者某任務的運行依賴于某信號量的出現時,刪除該任務是很危險的。
程序清單L6.9建立一個信號量
OS_EVENT*OSSemCreate(INT16Ucnt)
{
OS_EVENT*pevent;
OS_ENTER_CRITICAL();
pevent=OSEventFreeList;(1)
if(OSEventFreeList!=(OS_EVENT*)0){(2)
OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if(pevent!=(OS_EVENT*)0){(3)
pevent->OSEventType=OS_EVENT_TYPE_SEM;(4)
pevent->OSEventCnt=cnt;(5)
OSEventWaitListInit(pevent);(6)
}
return(pevent);(7)
}
6.6.2 等待一個信號量,OSSemPend()
程序清單L6.10是OSSemPend()函數的源代碼。它首先檢查指針pevent所指的任務控制塊是否是由OSSemCreate()建立的[L6.10(1)]。如果信號量當前是可用的(信號量的計數值大于0)[L6.10(2)],將信號量的計數值減1[L6.10(3)],然后函數將“無錯”錯誤代碼返回給它的調用函數。顯然,如果正在等待信號量,這時的輸出正是我們所希望的,也是運行OSSemPend()函數最快的路徑。
如果此時信號量無效(計數器的值是0),OSSemPend()函數要進一步檢查它的調用函數是不是中斷服務子程序[L6.10(4)]。在正常情況下,中斷服務子程序是不會調用OSSemPend()函數的。這里加入這些代碼,只是為了以防萬一。當然,在信號量有效的情況下,即使是中斷服務
子程序調用的OSSemPend(),函數也會成功返回,不會出任何錯誤。
如果信號量的計數值為0,而OSSemPend()函數又不是由中斷服務子程序調用的,則調
用OSSemPend()函數的任務要進入睡眠狀態(tài),等待另一個任務(或者中斷服務子程序)發(fā)出該信
號量(見下節(jié))。OSSemPend()允許用戶定義一個最長等待時間作為它的參數,這樣可以避免該
任務無休止地等待下去。如果該參數值是一個大于0的值,那么該任務將一直等到信號有效或
者等待超時。如果該參數值為0,該任務將一直等待下去。OSSemPend()函數通過將任務控制塊
中的狀態(tài)標志.OSTCBStat置1,把任務置于睡眠狀態(tài)[L6.10(5)],等待時間也同時置入任務控
制塊中[L6.10(6)],該值在OSTimeTick()函數中被逐次遞減。注意,OSTimeTick()函數對每個
任務的任務控制塊的.OSTCBDly域做遞減操作(只要該域不為0)[見3.10節(jié),時鐘節(jié)拍]。真
正將任務置入睡眠狀態(tài)的操作在OSEventTaskWait()函數中執(zhí)行[見6.03節(jié),讓一個任務等待
某個事件,OSEventTaskWait()][L6.10(7)]。
因為當前任務已經不是就緒態(tài)了,所以任務調度函數將下一個最高優(yōu)先級的任務調入,準備運行[L6.10(8)]。當信號量有效或者等待時間到后,調用OSSemPend()函數的任務將再一次成為最高優(yōu)先級任務。這時OSSched()函數返回。這之后,OSSemPend()要檢查任務控制塊中的狀態(tài)標志,看該任務是否仍處于等待信號量的狀態(tài)[L6.10(9)]。如果是,說明該任務還沒有被OSSemPost()函數發(fā)出的信號量喚醒。事實上,該任務是因為等待超時而由TimeTick()函數把它置為就緒狀態(tài)的。這種情況下,OSSemPend()函數調用 OSEventTO()函數將任務從等待任務列表中刪除[L6.10(10)],并返回給它的調用任務一個“超時”的錯誤代碼。如果任務的任務控制塊中的OS_STAT_SEM標志位沒有置位,就認為調用 OSSemPend()的任務已經得到了該信號量,將指向信號量ECB的指針從該任務的任務控制塊中刪除,并返回給調用函數一個“無錯”的錯誤代碼[L6.10(11)]。
程序清單L6.10等待一個信號量
voidOSSemPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err)
{
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_SEM){(1)
OS_EXIT_CRITICAL();
*err=OS_ERR_EVENT_TYPE;
}
if(pevent->OSEventCnt>0){(2)
pevent->OSEventCnt--;(3)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}elseif(OSIntNesting>0){(4)
OS_EXIT_CRITICAL();
*err=OS_ERR_PEND_ISR;
}else{
OSTCBCur->OSTCBStat|=OS_STAT_SEM;(5)
OSTCBCur->OSTCBDly=timeout;(6)
OSEventTaskWait(pevent);(7)
OS_EXIT_CRITICAL();
OSSched();(8)
OS_ENTER_CRITICAL();
if(OSTCBCur->OSTCBStatOS_STAT_SEM){(9)
OSEventTO(pevent);(10)
OS_EXIT_CRITICAL();
*err=OS_TIMEOUT;
}else{
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;(11)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}
}
}
6.6.3 發(fā)送一個信號量,OSSemPost()
程序清單L6.11是OSSemPost()函數的源代碼。它首先檢查參數指針pevent指向的任務控制塊是否是OSSemCreate()函數建立的[L6.11(1)],接著檢查是否有任務在等待該信號量[L6.11(2)]。如果該任務控制塊中的.OSEventGrp域不是0,說明有任務正在等待該信號量。這時,就要調用函數OSEventTaskRdy()[見6.02節(jié),使一個任務進入就緒狀態(tài),OSEventTaskRdy()],把其中的最高優(yōu)先級任務從等待任務列表中刪除[L6.11(3)]并使它進入就緒狀態(tài)。然后,調用OSSched()任務調度函數檢查該任務是否是系統(tǒng)中的最高優(yōu)先級的就緒任務[L6.11(4)]。如果是,這時就要進行任務切換[當OSSemPost()函數是在任務中調用的],準備執(zhí)行該就緒任務。如果不是,OSSched()直接返回,調用 OSSemPost()的任務得以繼續(xù)執(zhí)行。如果這時沒有任務在等待該信號量,該信號量的計數值就簡單地加1[L6.11(5)]。
上面是由任務調用OSSemPost()時的情況。當中斷服務子程序調用該函數時,不會發(fā)生上面的任務切換。如果需要,任務切換要等到中斷嵌套的最外層中斷服務子程序調用OSIntExit()函數后才能進行(見3.09節(jié),μC/OS-II中的中斷)。
評論