μC/OS-II的內(nèi)核結(jié)構(gòu)

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