時間管理的內容在代碼os_time.c中,包含操作系統時間的設置及獲取,對任務的延時,任務按分秒延時,取消任務的延時共5個系統調用。時間管理的最主要功能就是對任務進行延時。
時間管理中最重要的數據結構就是全局變量OSTime,OSTime的值就是操作系統的時間,它的定義在uC/OS-II的頭文件ucos_ii.h中,代碼如下所示:
50.png (4.12 KB, 下載次數: 95)
下載附件
2013-7-29 11:56 上傳
這里首先要知道關鍵字volatile的含義。volatile總是與優化有關,編譯器有一種技術叫做數據流分析,分析程序中的變量在哪里賦值、在哪里使用、在哪里失效,分析結果可以用于常量合并、常量傳播等優化。而關鍵字volatile卻是禁止做這些優化的。因為OSTime的值是易變的,加了關鍵字volatile后,不會被編譯器優化,每次取值都會直接在內存中對該變量的地址取值,從而保證不會因為編譯器優化而產生錯誤的結果。
OSTime在操作系統初始化時被設置為0。
時間管理中使用的另一個重要的數據結構就是任務控制塊,任務控制塊有一項是OSTCBDly,標志這個任務延時的時間。這個時間是以兩次時鐘中斷間隔的時間為單位的。另外,對任務的延時實際上阻塞了任務,因此要對就緒表和就緒組等數據結構進行相關的操作。
時間的設置和獲取都是關于OSTime的賦值,代碼比較簡單,如下所示:
51.png (12.55 KB, 下載次數: 103)
下載附件
2013-7-29 11:56 上傳
時間獲取函數OSTimeGet簡單地返回OSTime的值,需要注意的是,對OSTime的操作一定要使用臨界區。時間設置函數將參數ticks的值賦值給OSTime,這兩個函數并不常用。
任務延時函數OSTimeDly用于阻塞任務一定時間,這個時間以參數的形式給出。如果這個參數的值是N,那么在N個時間片(時鐘滴答)之后,任務才能回到就緒態獲得繼續運行的機會。如果參數的值是0,就不會阻塞任務。任務延時函數OSTimeDly的代碼如下所示:
52.png (28.45 KB, 下載次數: 89)
下載附件
2013-7-29 11:56 上傳
本段代碼層次清晰且比較簡單。OSLockNesting是調度鎖,也就是說,如果OSLockNesting>0,那么不允許進行任務調度。因為任務延時的時候要中止當前任務的執行,所以要進行調度,因此在調度鎖有效的情況下是不能執行任務延時的。如果延時時間大于0,那么就要進行一次任務調度,將當前的任務的就緒標志取消,也就是對就緒表和就緒組的相關操作。之后延時時間賦值給任務塊的OSTCBDly項以對延時計數。操作系統在每個時鐘中斷都要對每個OSTCBDly大于0的任務的OSTCBDly進行減1操作和進行任務調度,那么當任務的延時時間到了的時候(OSTCBDly為0)就可以恢復到就緒態。
需要注意的是,如果將任務延時1個時間片,調用OSTimeDly(1),會不會產生正確的結果呢?回答是否定的。這是因為任務在調用時間延時函數的時候可能已經馬上就要發生時間中斷了,那么設置OSTCBDly的值為1,想延時10ms,然后系統切換到一個新的任務運行。在可能極短的時間,如0.5ms的時候就進入時鐘中斷服務程序,立刻將OSTCBDly的值減到0了。調度器在調度的時候就會恢復這個才延時了0.5ms的任務。可見,OSTimeDly的誤差最大應該是1個時間片的長度,OSTCBDly(1)不會剛好延時10ms,如果真的需要延時一個時間片,最好調用OSTCBDly(2)。
任務延時函數OSTimeDly用于將任務阻塞一段時間,這個時間是以時間片為單位的。如果想以時、分、秒、毫秒為單位進行任務延時,需要調用以分秒作為單位的任務延時函數OSTimeDlyHMSM。
OSTimeDlyHMSM從功能上來說和OSTimeDly并沒有多大的差別,只是將時間單位進行了轉換,也就是說,轉換了以小時、分、秒、毫秒為單位的時間和以時間片為單位的時間。OSTimeDlyHMSM的參數分別是小時數(hours)、分鐘數(minutes)、秒數(seconds)和毫秒數(ms)。OSTimeDlyHMSM的代碼如下所示:
53.png (19.83 KB, 下載次數: 90)
下載附件
2013-7-29 11:56 上傳
54.png (19.13 KB, 下載次數: 89)
下載附件
2013-7-29 11:56 上傳
由代碼可知,OSTimeDlyHMSM在進行了參數檢查之后,將以小時、分、秒、毫秒為單位時間轉換為時間片ticks,最終調用OSTimedly來解決問題。
任務在延時之后,進入阻塞態。當延時時間到了就從阻塞態恢復到就緒態,可以被操作系統調度執行。但是,并非回到就緒態就只有這么一種可能,因為即便任務的延時時間沒到,還是可以通過函數OSTimeDlyResume恢復該任務到就緒態的。
另外,OSTimeDlyResume也不僅僅能恢復使用OSTimeDly或OSTimeDlyHMSM而延時的任務。對于因等待事件發生而阻塞的,并且設置了超時(timeout)時間的任務,也可以使用OSTimeDlyResume來恢復。對這些任務使用了OSTimeDlyResume,就好像已經等待超時了一樣。
但是,對于,采用OSTaskSuspend掛起的任務,是不允許采用OSTimeDlyResume來恢復的。
55.png (28.71 KB, 下載次數: 88)
下載附件
2013-7-29 11:56 上傳
56.png (24.21 KB, 下載次數: 102)
下載附件
2013-7-29 11:56 上傳
OSTimeDlyResume由于要處理任務錯綜復雜的關系,因此代碼稍顯復雜。代碼中一個非常重要的數據結構就是任務塊的OSTCBStat,如下所示:
57.png (10.39 KB, 下載次數: 93)
下載附件
2013-7-29 11:56 上傳
相關的宏定義如下所示:
58.png (20.79 KB, 下載次數: 86)
下載附件
2013-7-29 11:56 上傳
因此,如果一個任務只是設置了延時,那么該任務塊的OSTCBStat的值應該是0,也就是OS_STAT_RDY。被設置了延時的任務和就緒任務的區別在于,就緒任務的控制塊的OSTCBDly的值一定是0,而設置了延時的任務的OSTCBDly的值一定不是0.
如果一個任務在等待一個或多個事件的發生,那么該任務的控制塊的0、1、2、4、5位必然有1位或多位不為0。也就是ptcb->OSTCBStat&OS_STAT_PEND_ANY的值不為0,這是在判斷任務是不是在等待事件的發生。等待事件發生的任務可能設置了超時,也可能沒有設置超時,如果沒有設置超時那么就會在下面所示的代碼返回:
59.png (5.76 KB, 下載次數: 82)
下載附件
2013-7-29 11:56 上傳
所以不會被恢復到就緒態。設置了超時的OSTCBDly的值大于0,先將OSTCBDly的值置位為0,然后使用ptcb->OSTCBStat&=~OS_STAT_PEND_ANY,所以的5種事件等待標志全部強制清0,不再等待了。
另外,還需要判斷OSTCBStat的位3掛起標志。因為被掛起的任務必須用也只能用OSTaskResume來恢復。OS_STAT_SUSPEND的值是0x08,ptcb->OSTCBStat&OS_STAT_SUSPEND是將STCBStat的位3掛起標志位單獨取出來了,判斷它是不是0,如果是0,那么就不是被掛起的任務,否則就是被掛起的任務。對于掛起的任務只能處理到這里,對于其他的任務就開始對就緒表和就緒組進行處理,恢復任務到就緒態,然后執行任務調度。
|