μC/OS-II的內核結構

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