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这个数据结构,其主要做用是判断该任务块的状态——延时、超时,或者被挂起。而后针对不一样状况,以宏清任务块的数据结构。
流程图
七、参考
下一节将切回第二章,讨论任务的调度和多任务启动的内容。