μC/OS-II的任務(wù)之間的通訊與同步
OSMboxCreate()函數(shù)的返回值是一個(gè)指向事件控制塊的指針[L6.14(3)]。這個(gè)指針在調(diào)用函數(shù)OSMboxPend(),OSMboxPost(),OSMboxAccept()和OSMboxQuery()時(shí)使用。因此,該指針可以看作是對(duì)應(yīng)郵箱的句柄。值得注意的是,如果系統(tǒng)中已經(jīng)沒有事件控制塊可用,函數(shù)OSMboxCreate()將返回一個(gè)NULL指針。
郵箱一旦建立,是不能被刪除的。比如,如果有任務(wù)正在等待一個(gè)郵箱的信息,這時(shí)刪除該郵箱,將有可能產(chǎn)生災(zāi)難性的后果。
程序清單L6.14建立一個(gè)郵箱
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 等待一個(gè)郵箱中的消息,OSMboxPend()
程序清單L6.15是OSMboxPend()函數(shù)的源代碼。 同樣, 它和OSSemPend()也很相似,因此,在這里只講述其中的不同之處。OSMboxPend()首先檢查該事件控制塊是由 OSMboxCreate()函數(shù)建立的[L6.15(1)]。當(dāng).OSEventPtr域是一個(gè)非NULL的指針時(shí),說明該郵箱中有可用的消息[L6.15(2)]。這種情況下,OSMboxPend()函數(shù)將該域的值復(fù)制到局部變量msg中,然后將.OSEventPtr置為NULL[L6.15(3)]。這正是我們所期望的,也是執(zhí)行 OSMboxPend()函數(shù)最快的路徑。
如果此時(shí)郵箱中沒有消息是可用的(.OSEventPtr域是NULL指針),OSMboxPend()函數(shù)檢查它的調(diào)用者是否是中斷服務(wù)子程序[L6.15(4)]。象OSSemPend()函數(shù)一樣,不能在中斷服務(wù)子程序中調(diào)用OSMboxPend(),因?yàn)橹袛喾?wù)子程序是不能等待的。這里的代碼同樣是為了以防萬一。但是,如果郵箱中有可用的消息,即使從中斷服務(wù)子程序中調(diào)用OSMboxPend()函數(shù),也一樣是成功的。
如果郵箱中沒有可用的消息,OSMboxPend()的調(diào)用任務(wù)就被掛起,直到郵箱中有了消息或者等待超時(shí)[L6.15(5)]。當(dāng)有其它的任務(wù)向該郵箱發(fā)送了消息后(或者等待時(shí)間超時(shí)),這時(shí),該任務(wù)再一次成為最高優(yōu)先級(jí)任務(wù),OSSched()返回。這時(shí),OSMboxPend()函數(shù)要檢查是否有消息被放到該任務(wù)的任務(wù)控制塊中[L6.15(6)]。如果有,那么該次函數(shù)調(diào)用成功,對(duì)應(yīng)的消息被返回到調(diào)用函數(shù)。
程序清單L6.15等待一個(gè)郵箱中的消息
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()函數(shù)中,通過檢查任務(wù)控制塊中的.OSTCBStat域中的OS_STAT_MBOX位,可以知道是否等待超時(shí)。如果該域被置1,說明任務(wù)等待已經(jīng)超時(shí)[L6.15(7)]。這時(shí),通過調(diào)用函數(shù)OSEventTo()可以將任務(wù)從郵箱的等待列表中刪除[L6.15(8)]。因?yàn)榇藭r(shí)郵箱中沒有消息,所以返回的指針是NULL[L6.15(9)]。如果OS_STAT_MBOX位沒有被置1,說明所等待的消息已經(jīng)被發(fā)出。OSMboxPend()的調(diào)用函數(shù)得到指向消息的指針[L6.15(10)]。此后,OSMboxPend()函數(shù)通過將郵箱事件控制塊的.OSEventPtr域置為NULL清空該郵箱,并且要將任務(wù)任務(wù)控制塊中指向郵箱事件控制塊的指針刪除[L6.15(12)]。
6.7.3 發(fā)送一個(gè)消息到郵箱中,OSMboxPost()
程序清單L6.16是OSMboxPost()函數(shù)的源代碼。檢查了事件控制塊是否是一個(gè)郵箱后[L6.16(1)],OSMboxPost()函數(shù)還要檢查是否有任務(wù)在等待該郵箱中的消息[L6.16(2)]。如果事件控制塊中的OSEventGrp域包含非零值,就暗示著有任務(wù)在等待該消息。這時(shí),調(diào)用OSEventTaskRdy()將其中的最高優(yōu)先級(jí)任務(wù)從等待列表中刪除[見6.02節(jié),使一個(gè)任務(wù)進(jìn)入就緒狀態(tài),OSEventTaskRdy()][L6.16(3)],加入系統(tǒng)的就緒任務(wù)列表中,準(zhǔn)備運(yùn)行。然后,調(diào)用OSSched()函數(shù)[L6.16(4)],檢查該任務(wù)是否是系統(tǒng)中最高優(yōu)先級(jí)的就緒任務(wù)。如果是,執(zhí)行任
務(wù)切換[僅當(dāng)OSMboxPost()函數(shù)是由任務(wù)調(diào)用時(shí)],該任務(wù)得以執(zhí)行。如果該任務(wù)不是最高優(yōu)先
級(jí)的任務(wù),OSSched()返回,OSMboxPost()的調(diào)用函數(shù)繼續(xù)執(zhí)行。如果沒有任何任務(wù)等待該消息,
指向消息的指針就被保存到郵箱中[L6.16(6)](假設(shè)此時(shí)郵箱中的指針不是非NULL的[L6.16(5)])。這樣,下一個(gè)調(diào)用OSMboxPend()函數(shù)的任務(wù)就可以立刻得到該消息了。
注意,如果OSMboxPost()函數(shù)是從中斷服務(wù)子程序中調(diào)用的,那么,這時(shí)并不發(fā)生上下文的切換。如果需要,中斷服務(wù)子程序引起的上下文切換只發(fā)生在中斷嵌套的最外層中斷服務(wù)子程序?qū)SIntExit()函數(shù)的調(diào)用時(shí)(見3.09節(jié),μC/OS-II中的中斷)。
評(píng)論