μC/OS-II的內(nèi)核結(jié)構(gòu)
圖F3.4統(tǒng)計(jì)任務(wù)的初始化
TaskStart()負(fù)責(zé)初始化和啟動(dòng)時(shí)鐘節(jié)拍[圖F3.4(5)]。在這里啟動(dòng)時(shí)鐘節(jié)拍是必要的,因?yàn)橛脩舨粫?huì)希望在多任務(wù)還沒(méi)有開始時(shí)就接收到時(shí)鐘節(jié)拍中斷。接下去TaskStart()調(diào)用統(tǒng)計(jì)初始化函數(shù)OSStatInit()[圖F3.4(6)]。統(tǒng)計(jì)初始化函數(shù)OSStatInit()決定在沒(méi)有其它應(yīng)用任務(wù)運(yùn)行時(shí),空閑計(jì)數(shù)器(OSIdleCtr)的計(jì)數(shù)有多快。奔騰II微處理器以333MHz運(yùn)行時(shí),加1操作可以使該計(jì)數(shù)器的值達(dá)到每秒15,000,000次。OSIdleCtr的值離32位計(jì)數(shù)器的溢出極限值4,294,967,296還差得遠(yuǎn)。微處理器越來(lái)越快,用戶要注意這里可能會(huì)是將來(lái)的一個(gè)潛在問(wèn)題。
系統(tǒng)統(tǒng)計(jì)初始化任務(wù)函數(shù)OSStatInit()調(diào)用延遲函數(shù)OSTimeDly()將自身延時(shí)2個(gè)時(shí)鐘節(jié)拍以停止自身的運(yùn)行[圖F3.4(7)]。這是為了使OSStatInit()與時(shí)鐘節(jié)拍同步。μC/OS-Ⅱ然后選下一個(gè)優(yōu)先級(jí)最高的進(jìn)入就緒態(tài)的任務(wù)運(yùn)行,這恰好是統(tǒng)計(jì)任務(wù)OSTaskStat()。
讀者會(huì)在后面讀到OSTaskStat()的代碼,但粗看一下,OSTaskStat()所要做的第一件事就是查看統(tǒng)計(jì)任務(wù)就緒標(biāo)志是否為“假”,如果是的話,也要延時(shí)兩個(gè)時(shí)鐘節(jié)拍[圖F3.4(8)]。一定會(huì)是這樣,因?yàn)闃?biāo)志OSStatRdy已被OSInit()函數(shù)初始化為“假”,所以
實(shí)際上DSTaskStat也將自己推入休眠態(tài)(Sleep)兩個(gè)時(shí)鐘節(jié)拍[圖F3.4(9)]。于是任務(wù)切換到空閑任務(wù),OSTaskIdle()開始運(yùn)行,這是唯一一個(gè)就緒態(tài)任務(wù)了。CPU處在空閑任務(wù)OSTaskIdle中,直到TaskStart()的延遲兩個(gè)時(shí)鐘節(jié)拍完成[圖3.4(10)]。兩個(gè)時(shí)鐘節(jié)拍之后,TaskStart()恢復(fù)運(yùn)行[圖F3.4(11)]。 在執(zhí)行OSStartInit()時(shí),空閑計(jì)數(shù)器OSIdleCtr被清零[圖F3.4(12)]。然后,OSStatInit()將自身延時(shí)整整一秒[圖F3.4(13)]。因?yàn)闆](méi)有其它進(jìn)入就緒態(tài)的任務(wù),OSTaskIdle()又獲得了CPU的控制權(quán)[圖F3.4(14)]。一秒鐘以后,TaskStart()繼續(xù)運(yùn)行,還是在OSStatInit()中,空閑計(jì)數(shù)器將1秒鐘內(nèi)計(jì)數(shù)的值存入空閑計(jì)數(shù)器最大值OSIdleCtrMax中[圖F3.4(15)]。
OSStarInit()將統(tǒng)計(jì)任務(wù)就緒標(biāo)志OSStatRdy設(shè)為“真”[圖F3.4(16)],以此來(lái)允許兩個(gè)時(shí)鐘節(jié)拍以后OSTaskStat()開始計(jì)算CPU的利用率。
統(tǒng)計(jì)任務(wù)的初始化函數(shù)OSStatInit()的代碼如程序清單L3.13所示。
程序清單L3.13統(tǒng)計(jì)任務(wù)的初始化.
voidOSStatInit(void)
{
OSTimeDly(2);
OS_ENTER_CRITICAL();
OSIdleCtr=0L;
OS_EXIT_CRITICAL();
OSTimeDly(OS_TICKS_PER_SEC);
OS_ENTER_CRITICAL();
OSIdleCtrMax=OSIdleCtr;
OSStatRdy=TRUE;
OS_EXIT_CRITICAL();
}
統(tǒng)計(jì)任務(wù)OSStat()的代碼程序清單L3.14所示。在前面一段中,已經(jīng)討論了為什么要等待統(tǒng)計(jì)任務(wù)就緒標(biāo)志OSStatRdy[L3.14(1)]。這個(gè)任務(wù)每秒執(zhí)行一次,以確定所有應(yīng)用程序中的任務(wù)消耗了多少CPU時(shí)間。當(dāng)用戶的應(yīng)用程序代碼加入以后,運(yùn)行空閑任務(wù)的CPU時(shí)間就少了,OSIdleCtr就不會(huì)像原來(lái)什么任務(wù)都不運(yùn)行時(shí)有那么多計(jì)數(shù)。要知道,OSIdleCtr的最大計(jì)數(shù)值是OSStatInit()在初始化時(shí)保存在計(jì)數(shù)器最大值OSIdleCtrMax中的。CPU利用率(表達(dá)式[3.1])是保存在變量OSCPUsage[L3.14(2)]中的:
[3.1]表達(dá)式 Needtotypesettheequation.
一旦上述計(jì)算完成,OSTaskStat()調(diào)用任務(wù)統(tǒng)計(jì)外界接入函數(shù)OSTaskStatHook()[L3.14(3)],這是一個(gè)用戶可定義的函數(shù),這個(gè)函數(shù)能使統(tǒng)計(jì)任務(wù)得到擴(kuò)展。這樣,用戶可以計(jì)算并顯示所有任務(wù)總的執(zhí)行時(shí)間,每個(gè)任務(wù)執(zhí)行時(shí)間的百分比以及其它信息(參見1.09節(jié)例3)。
程序清單L3.14統(tǒng)計(jì)任務(wù)
voidOSTaskStat(void*pdata)
{
INT32Urun;
INT8Susage;
pdata=pdata;
while(OSStatRdy==FALSE){(1)
OSTimeDly(2*OS_TICKS_PER_SEC);
}
for(;;){
OS_ENTER_CRITICAL();
OSIdleCtrRun=OSIdleCtr;
run=OSIdleCtr;
OSIdleCtr=0L;
OS_EXIT_CRITICAL();
if(OSIdleCtrMax>0L){
usage=(INT8S)(100L-100L*run/OSIdleCtrMax);(2)
if(usage>100){
OSCPUUsage=100;
}elseif(usage0){
OSCPUUsage=0;
}else{
OSCPUUsage=usage;
}
}else{
OSCPUUsage=0;
}
OSTaskStatHook();(3)
OSTimeDly(OS_TICKS_PER_SEC);
}
}
3.9 μC/OS中的中斷處理
μC/OS中,中斷服務(wù)子程序要用匯編語(yǔ)言來(lái)寫。然而,如果用戶使用的C語(yǔ)言編譯器支持在線匯編語(yǔ)言的話,用戶可以直接將中斷服務(wù)子程序代碼放在C語(yǔ)言的程序文件中。中斷服務(wù)子程序的示意碼如程序清單L3.15所示。
程序清單L3.15μC/OS-II中的中斷服務(wù)子程序.
用戶中斷服務(wù)子程序:
保存全部CPU寄存器;(1)
調(diào)用OSIntEnter或OSIntNesting直接加1; (2)
執(zhí)行用戶代碼做中斷服務(wù);(3)
調(diào)用OSIntExit(); (4)
執(zhí)行中斷返回指令; (6)
用戶代碼應(yīng)該將全部CPU寄存器推入當(dāng)前任務(wù)棧[L3.15(1)]。注意,有些微處理器,例如Motorola68020(及68020以上的微處理器),做中斷服務(wù)時(shí)使用另外的堆棧。
μC/OS-Ⅱ可以用在這類微處理器中,當(dāng)任務(wù)切換時(shí),寄存器是保存在被中斷了的那個(gè)任務(wù)的棧中的。
μC/OS-Ⅱ需要知道用戶在做中斷服務(wù),故用戶應(yīng)該調(diào)用OSIntEnter(),或者將全程變量OSIntNesting[L3.15(2)]直接加1,如果用戶使用的微處理器有存儲(chǔ)器直接加1的單條指令的話。如果用戶使用的微處理器沒(méi)有這樣的指令,必須先將OSIntNesting讀入寄存器,再將寄存器加1,然后再寫回到變量OSIatNesting中去,就不如調(diào)用OSIatEnter()。
OSIntNesting是共享資源。OSIntEnter()把上述三條指令用開中斷、關(guān)中斷保護(hù)起來(lái),以保證處理OSIntNesting時(shí)的排它性。直接給OSIntNesting加1比調(diào)用OSIntEnter()快得多,可能時(shí),直接加1更好。要當(dāng)心的是,在有些情況下,從OSIntEnter()返回時(shí),會(huì)把中斷開了。遇到這種情況,在調(diào)用OSIntEnter()之前要先清中斷源,否則,中斷將連續(xù)反復(fù)打入,用戶應(yīng)用程序就會(huì)崩潰!
評(píng)論