μC/OS-II的任務之間的通訊與同步
OSMboxCreate()函數的返回值是一個指向事件控制塊的指針[L6.14(3)]。這個指針在調用函數OSMboxPend(),OSMboxPost(),OSMboxAccept()和OSMboxQuery()時使用。因此,該指針可以看作是對應郵箱的句柄。值得注意的是,如果系統中已經沒有事件控制塊可用,函數OSMboxCreate()將返回一個NULL指針。
郵箱一旦建立,是不能被刪除的。比如,如果有任務正在等待一個郵箱的信息,這時刪除該郵箱,將有可能產生災難性的后果。
程序清單L6.14建立一個郵箱
OS_EVENT*OSMboxCreate(void*msg)
{
OS_EVENT*pevent;
OS_ENTER_CRITICAL();
pevent=OSEventFreeList;
if(OSEventFreeList!=(OS_EVENT*)0){
OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if(pevent!=(OS_EVENT*)0){
pevent->OSEventType=OS_EVENT_TYPE_MBOX;(1)
pevent->OSEventPtr=msg;(2)
OSEventWaitListInit(pevent);
}
return(pevent);(3)
}
6.7.2 等待一個郵箱中的消息,OSMboxPend()
程序清單L6.15是OSMboxPend()函數的源代碼。 同樣, 它和OSSemPend()也很相似,因此,在這里只講述其中的不同之處。OSMboxPend()首先檢查該事件控制塊是由 OSMboxCreate()函數建立的[L6.15(1)]。當.OSEventPtr域是一個非NULL的指針時,說明該郵箱中有可用的消息[L6.15(2)]。這種情況下,OSMboxPend()函數將該域的值復制到局部變量msg中,然后將.OSEventPtr置為NULL[L6.15(3)]。這正是我們所期望的,也是執行 OSMboxPend()函數最快的路徑。
如果此時郵箱中沒有消息是可用的(.OSEventPtr域是NULL指針),OSMboxPend()函數檢查它的調用者是否是中斷服務子程序[L6.15(4)]。象OSSemPend()函數一樣,不能在中斷服務子程序中調用OSMboxPend(),因為中斷服務子程序是不能等待的。這里的代碼同樣是為了以防萬一。但是,如果郵箱中有可用的消息,即使從中斷服務子程序中調用OSMboxPend()函數,也一樣是成功的。
如果郵箱中沒有可用的消息,OSMboxPend()的調用任務就被掛起,直到郵箱中有了消息或者等待超時[L6.15(5)]。當有其它的任務向該郵箱發送了消息后(或者等待時間超時),這時,該任務再一次成為最高優先級任務,OSSched()返回。這時,OSMboxPend()函數要檢查是否有消息被放到該任務的任務控制塊中[L6.15(6)]。如果有,那么該次函數調用成功,對應的消息被返回到調用函數。
程序清單L6.15等待一個郵箱中的消息
void*OSMboxPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err)
{
void*msg;
OS_ENTER_CRITICAL();
if(pevent->OSEventType!=OS_EVENT_TYPE_MBOX){(1)
OS_EXIT_CRITICAL();
*err=OS_ERR_EVENT_TYPE;
return((void*)0);
}
msg=pevent->OSEventPtr;
if(msg!=(void*)0){(2)
pevent->OSEventPtr=(void*)0;(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_MBOX;(5)
OSTCBCur->OSTCBDly=timeout;
OSEventTaskWait(pevent);
OS_EXIT_CRITICAL();
OSSched();
OS_ENTER_CRITICAL();
if((msg=OSTCBCur->OSTCBMsg)!=(void*)0){(6)
OSTCBCur->OSTCBMsg=(void*)0;
OSTCBCur->OSTCBStat=OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}elseif(OSTCBCur->OSTCBStatOS_STAT_MBOX){(7)
OSEventTO(pevent);(8)
OS_EXIT_CRITICAL();
msg=(void*)0;(9)
*err=OS_TIMEOUT;
}else{
msg=pevent->OSEventPtr;(10)
pevent->OSEventPtr=(void*)0;
(11)
OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;(12)
OS_EXIT_CRITICAL();
*err=OS_NO_ERR;
}
}
return(msg);
}
在OSMboxPend()函數中,通過檢查任務控制塊中的.OSTCBStat域中的OS_STAT_MBOX位,可以知道是否等待超時。如果該域被置1,說明任務等待已經超時[L6.15(7)]。這時,通過調用函數OSEventTo()可以將任務從郵箱的等待列表中刪除[L6.15(8)]。因為此時郵箱中沒有消息,所以返回的指針是NULL[L6.15(9)]。如果OS_STAT_MBOX位沒有被置1,說明所等待的消息已經被發出。OSMboxPend()的調用函數得到指向消息的指針[L6.15(10)]。此后,OSMboxPend()函數通過將郵箱事件控制塊的.OSEventPtr域置為NULL清空該郵箱,并且要將任務任務控制塊中指向郵箱事件控制塊的指針刪除[L6.15(12)]。
6.7.3 發送一個消息到郵箱中,OSMboxPost()
程序清單L6.16是OSMboxPost()函數的源代碼。檢查了事件控制塊是否是一個郵箱后[L6.16(1)],OSMboxPost()函數還要檢查是否有任務在等待該郵箱中的消息[L6.16(2)]。如果事件控制塊中的OSEventGrp域包含非零值,就暗示著有任務在等待該消息。這時,調用OSEventTaskRdy()將其中的最高優先級任務從等待列表中刪除[見6.02節,使一個任務進入就緒狀態,OSEventTaskRdy()][L6.16(3)],加入系統的就緒任務列表中,準備運行。然后,調用OSSched()函數[L6.16(4)],檢查該任務是否是系統中最高優先級的就緒任務。如果是,執行任
務切換[僅當OSMboxPost()函數是由任務調用時],該任務得以執行。如果該任務不是最高優先
級的任務,OSSched()返回,OSMboxPost()的調用函數繼續執行。如果沒有任何任務等待該消息,
指向消息的指針就被保存到郵箱中[L6.16(6)](假設此時郵箱中的指針不是非NULL的[L6.16(5)])。這樣,下一個調用OSMboxPend()函數的任務就可以立刻得到該消息了。
注意,如果OSMboxPost()函數是從中斷服務子程序中調用的,那么,這時并不發生上下文的切換。如果需要,中斷服務子程序引起的上下文切換只發生在中斷嵌套的最外層中斷服務子程序對OSIntExit()函數的調用時(見3.09節,μC/OS-II中的中斷)。
評論