0、前言html
這節既然談到時間管理,便須要一個度量,來衡量系統執行的時間。咱們能夠用時間片,也能夠用現實生活中的分秒。數據結構
ucos中的時間片的具體設置與硬件環境有關,這裏先不進行討論。函數
然而在多任務狀況下,每一個時間片(也叫時間中斷)都要執行任務的調度,這種調度稱爲任務級任務調度(上一節已學習了中斷級任務調度)。oop
ucos在每一個時間片都要進行任務調度。調度的結果或者是返回原來的任務繼續執行,或者是由於找到了就緒的更高優先級的任務,而讓任務運行。這個時間片能夠是10ms或其餘值。若是時間太長,高優先級的就緒任務可能等待時間過長,若是時間過短,花費在操做系統調度上的時間就顯得過長,系統的吞吐量就變小。學習
有關任務級任務調度的具體學習將在下一節討論,這一節主要學習時間管理。ui
一、思惟導圖this
二、時間管理主要數據結構spa
1 #if OS_TIME_GET_SET_EN > 0 2 OS_EXT volatile INT32U OSTime; /* Current value of system time (in ticks) */ 3 #endif
三、時間的獲取和設置操作系統
源碼:.net
時間的得到
1 #if OS_TIME_GET_SET_EN > 0 2 INT32U OSTimeGet (void) 3 { 4 INT32U ticks; 5 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 6 OS_CPU_SR cpu_sr = 0; 7 #endif 8 9 10 11 OS_ENTER_CRITICAL(); 12 ticks = OSTime; 13 OS_EXIT_CRITICAL(); 14 return (ticks); 15 } 16 #endif
時間的設置
1 #if OS_TIME_GET_SET_EN > 0 2 void OSTimeSet (INT32U ticks) 3 { 4 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 5 OS_CPU_SR cpu_sr = 0; 6 #endif 7 8 9 10 OS_ENTER_CRITICAL(); 11 OSTime = ticks; 12 OS_EXIT_CRITICAL(); 13 } 14 #endif
四、任務延時函數OSTimeDly
源碼:
1 void OSTimeDly (INT16U ticks) 2 { 3 INT8U y; 4 #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ 5 OS_CPU_SR cpu_sr = 0; 6 #endif 7 8 9 10 if (OSIntNesting > 0) { /* See if trying to call from an ISR */ 11 return; 12 } 13 if (ticks > 0) { /* 0 means no delay! */ 14 OS_ENTER_CRITICAL(); 15 y = OSTCBCur->OSTCBY; /* Delay current task */ 16 OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX; 17 if (OSRdyTbl[y] == 0) { 18 OSRdyGrp &= ~OSTCBCur->OSTCBBitY; 19 } 20 OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */ 21 OS_EXIT_CRITICAL(); 22 OS_Sched(); /* Find next task to run! */ 23 } 24 }
line 10~line 12,表示若是有中斷嵌套的話,則不延時;中斷的存在即是爲了實時處理任務。
不過書上還有一個條件判斷,這兒沒有。
1 if OSLockingNesting > 0u 2 return ;
line 13~line 19,在就緒表中取消當前任務的就緒標誌,即對當前任務進行延時。
line 20,給任務塊的OSTCBDly項賦值延時時間。操做系統在每一個時間片都要對每一個OSTCBDly大於0的任務塊OSTCBDly項進行減1操做和進行調度,當OSTCBDly減到0時,就能夠恢復至就緒態。
這裏須要注意的是,若是須要延時一個時間片,最好調用兩個OSTCBDly(2),具體解釋以下,不愛看記住結論就好。
1 須要注意的是,若是將任務延時1個時間片,調用OSTimeDly(1),會不會產生正確的結果呢? 2 3 回答是否認的。這是由於任務在調用時間延時函數的時候可能已經立刻就要發生時間中斷了,那麼設置OSTCBDly的值爲1,想延時10ms,而後系統切換到一個新的任務運行。在可能極短的時間,如0.5ms的時候就進入時鐘中斷服務程序,馬上將OSTCBDly的值減到0了。調度器在調度的時候就會恢復這個才延時了0.5ms的任務。可見,OSTimeDly的偏差最大應該是1個時間片的長度,OSTCBDly(1)不會恰好延時10ms, 4 5 若是真的須要延時一個時間片,最好調用OSTCBDly(2)。
流程圖
五、任務按分秒延時函數OSTimeDlyHMSM()
這一樣也是將任務延時(阻塞)一段時間,上一個函數是以時間片爲單位,而這個函數是以小時、分、秒爲單位。
源碼
1 #if OS_TIME_DLY_HMSM_EN > 0 2 INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms) 3 { 4 INT32U ticks; 5 INT16U loops; 6 7 8 if (OSIntNesting > 0) { /* See if trying to call from an ISR */ 9 return (OS_ERR_TIME_DLY_ISR); 10 } 11 #if OS_ARG_CHK_EN > 0 12 if (hours == 0) { 13 if (minutes == 0) { 14 if (seconds == 0) { 15 if (ms == 0) { 16 return (OS_ERR_TIME_ZERO_DLY); 17 } 18 } 19 } 20 } 21 if (minutes > 59) { 22 return (OS_ERR_TIME_INVALID_MINUTES); /* Validate arguments to be within range */ 23 } 24 if (seconds > 59) { 25 return (OS_ERR_TIME_INVALID_SECONDS); 26 } 27 if (ms > 999) { 28 return (OS_ERR_TIME_INVALID_MS); 29 } 30 #endif 31 /* Compute the total number of clock ticks required.. */ 32 /* .. (rounded to the nearest tick) */ 33 ticks = ((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC 34 + OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L; 35 loops = (INT16U)(ticks >> 16); /* Compute the integral number of 65536 tick delays */ 36 ticks = ticks & 0xFFFFL; /* Obtain the fractional number of ticks */ 37 OSTimeDly((INT16U)ticks); 38 while (loops > 0) { 39 OSTimeDly((INT16U)32768u); 40 OSTimeDly((INT16U)32768u); 41 loops--; 42 } 43 return (OS_ERR_NONE); 44 } 45 #endif
因爲二者功能是同樣的,因此結構也很相似。
line 8~ line 30負責檢查參數,然後將形參轉變成時間片,再進行延時。
六、延時恢復函數OSTimeDlyResume()
任務在延時以後,進入阻塞態。當延時時間到了就從阻塞態恢復到就緒態,能夠被操做系統調度執行。可是,並不是回到就緒態就只有這麼一種方法,一樣能夠經過調用延時恢復函數OSTimeDlyResume()恢復該任務到就緒態。
源碼
1 #if OS_TIME_DLY_RESUME_EN > 0 2 INT8U OSTimeDlyResume (INT8U prio) 3 { 4 OS_TCB *ptcb; 5 #if OS_CRITICAL_METHOD == 3 /* Storage for CPU status register */ 6 OS_CPU_SR cpu_sr = 0; 7 #endif 8 9 10 11 if (prio >= OS_LOWEST_PRIO) { 12 return (OS_ERR_PRIO_INVALID); 13 } 14 OS_ENTER_CRITICAL(); 15 ptcb = OSTCBPrioTbl[prio]; /* Make sure that task exist */ 16 if (ptcb == (OS_TCB *)0) { 17 OS_EXIT_CRITICAL(); 18 return (OS_ERR_TASK_NOT_EXIST); /* The task does not exist */ 19 } 20 if (ptcb == OS_TCB_RESERVED) { 21 OS_EXIT_CRITICAL(); 22 return (OS_ERR_TASK_NOT_EXIST); /* The task does not exist */ 23 } 24 if (ptcb->OSTCBDly == 0) { /* See if task is delayed */ 25 OS_EXIT_CRITICAL(); 26 return (OS_ERR_TIME_NOT_DLY); /* Indicate that task was not delayed */ 27 } 28 29 ptcb->OSTCBDly = 0; /* Clear the time delay */ 30 if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { 31 ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */ 32 ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout */ 33 } else { 34 ptcb->OSTCBStatPend = OS_STAT_PEND_OK; 35 } 36 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */ 37 OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */ 38 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; 39 OS_EXIT_CRITICAL(); 40 OS_Sched(); /* See if this is new highest priority */ 41 } else { 42 OS_EXIT_CRITICAL(); /* Task may be suspended */ 43 } 44 return (OS_ERR_NONE); 45 } 46 #endif
事實上OSTimeDlyResume()能處理的狀況不少,一來能夠恢復使用延時的任務(取消其延時,馬上就緒),二來能恢復設置了超時的函數,但對於採用OSTaskSuspend掛起的任務,則不容許使用這個函數恢復。所以,這個函數的代碼有些複雜。
函數形參爲被恢復任務的優先級。
line11~line 27爲參數檢查,避免恢復那些既沒有延時也沒有超時的函數。
以後多個條件判斷中都出現了OSTCBStat這個數據結構,其主要做用是判斷該任務塊的狀態——延時、超時,或者被掛起。而後針對不一樣狀況,以宏清任務塊的數據結構。
流程圖
七、參考
下一節將切回第二章,討論任務的調度和多任務啓動的內容。