ucos---Scheduling(调度)

调度器,也称分发器,是负责决定接下来运行哪个任务。UCOS 是一个基于优先级的抢占式内核。如我们所见,每个任务都根据其重要性赋予一个优先级别。任务的优先级别依赖于其具体应用场景,另外,UCOS 支持同一优先级多个任务。

抢占意味着当事件发生,将响应的高优先级的任务切换为就绪状态,接着此任务立即获得 CPU的控制权。当前任务挂起,高优先级的新任务运行。

1 抢占调度

UCOS 处理事件发布有两种方式:直接发布和延迟发布。

1.1 直接发布

抢占调度---直接发布
  1. 一个低优先级的任务正在执行的时候发生了一个中断。(1)
  2. 如果中断处于使能,则依据 CPU 中断向量表将跳转到 ISR,执行中断服务程序。(2)
  3. 中断服务程序执行,通知响应的任务,切换到就绪状态。(3)
  4. ISR 执行完成后,UCOS 调用任务切换服务。(4)
  5. CPU 切换到执行优先级更高的就绪任务。(5)(6)
  6. 高优先级任务执行完成,切换到 UCOS 中,查询是否有另一个中断发生。(7)(8)
  7. 若没有中断发生,则 UCOS 切换到先前被中断任务继续执行。(9)(10)
  8. 先前被中断任务从中断处继续执行。(11)

1.2 延时发布

抢占调度---延时发布
  1. 此种方式是在 ISR 中,不会直接通知对应的等待事件的任务,而是将该事件放到一个队列中来处理。
  2. 当 ISR 完成后,则切换到UCOS中来工作。
  3. ISR 使得 ISR 处理任务切换为就绪状态,则 UCOS 切换到该任务。
  4. ISR 处理任务通知高优先级任务使其处于就绪状态,ISR队列所有中断处理完毕,并切换到高优先级就绪任务执行。

2 调度入口

  • 某一任务信号通知或发送消息到另一个任务
  • 某一任务调用 OSTimeDly() or OSTimeDlyHMSM()
  • 某一任务需要等待一个事件的发生,且此事件当前尚未发生。
  • 如果某一任务中止等待
  • 如果某一任务被新建
  • 若某一任务被删除
  • 若某一内核对象被删除
  • 某一任务的优先级被改变
  • 某一任务调用 OSTaskSuspend () 挂起本身
  • 某一任务被另一任务通知继续
  • 在 ISRs 嵌套的最后一个 ISR 执行完成
  • 调度锁解锁时
  • 某一任务调用 OSSchedRoundRobinYield() 放弃时间片
  • 用户调用 OSSched()

3 轮转调度

当两个或者更多的任务有相同的优先级时,UCOS 允许一个任务运行一个时间片后调度另外一个任务,这个过程称之为时间切片或轮转调度。如果某个任务不需要整个时间片,则它可以主动放弃 CPU 以便执行下一任务。UCOS 允许用户决定是否使能时间片轮转调度算法。

下图显示了一个轮转调度的样例,一共有 3 个优先级相同的任务处于就绪状态。为了方便例释,每四个时钟为一个时间片,图中加深的时钟线。


时间片轮转调度

(1)Task #3 正在执行,但是此时,发生了一个时钟中断,且 Task #3 的时间片尚未用尽。
(2)一个 4th 时钟中断发生,则 Task #3 的时间片耗尽
(3)UCOS 暂停 Task #3,切换到执行 Task #1
(4)Task #1 持续执行直到时间用完
(5)(6)(7)Task #3 执行过程中,通过调用 OSSchedRoundRobinYield() 决定放弃剩余时间片,这将会切换到下一个就绪任务。需要注意的是,当 UCOS 调度到 Task #1 时,重置时间片为 4 个滴答,以至于下一个时间片从这个点消耗 4 个时间片。
(8)Task #1 zhixing 执行整个时间片

用户可以通过调用 OSSchedRoundRobinCfg() 设置时间片大小。UCOS 也允许在创建任务时指定该任务自己的时间片。

3 调度原理

调度是通过调用 OSSched() 和 OSIntExit() 两个函数进行。OSSched() 是任务代码调用,而 OSIntExit() 是 ISR 中调用。两个都在可以再 os_core.c中发现。

下图是就绪队列的两个结构:


优先级就绪位图和就绪列表

3.1 OSSched()

以下是伪代码:

void OSSched(void)
{
    Disable interrupts;
    //确认没有从 ISR 中调用 OSSched()
    if(OSIntNestingCtr > 0)   
    {
        return;
    }
    //确认调度锁打开
    if(OSSchedLockNestingCtr > 0)  
    {
        return;
    }
   // 从OSPrioTbl[] 中获取最高优先级的就绪任务
    Get highest priority ready;     //(3)
   //获取待执行任务的 TCB指针,并从就绪列表中删除
    Get pointer to OS_TCB of next highest priority task;   //(4)
   //不允许同一任务切换
    if(OSTCBNHighRdyPtr != OSTCBCurPtr)     //5
    {
        perform task level context switch;      
    }
    Enable interrupts;
}

代码也表明次函数只能在任务级代码调用,在调度和上下文切换时需要禁止中断,因为该过程必须原子化。

3.2 OSIntExit()

以下是代码:

void  OSIntExit (void)
{
   CPU_SR_ALLOC();
  //Has the OS started?                           
   if (OSRunning != OS_STATE_OS_RUNNING) {               
       return;                                            
   }

   CPU_INT_DIS();
  /* Prevent OSIntNestingCtr from wrapping                  */
   if (OSIntNestingCtr == (OS_NESTING_CTR)0) {             
       CPU_INT_EN();
       return;
   }
   OSIntNestingCtr--;
   /* ISRs still nested?          */
   if (OSIntNestingCtr > (OS_NESTING_CTR)0) {             
       CPU_INT_EN();            /* Yes    */                           
       return;
   }
  /* Scheduler still locked?             */
   if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {       
       CPU_INT_EN();           /* Yes           */
       return;
   }
 /* Find highest priority                                  */
   OSPrioHighRdy   = OS_PrioGetHighest();     
 /* Get highest priority task ready-to-run                 */           
   OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;   
 /* Current task still the highest priority?               */
   if (OSTCBHighRdyPtr == OSTCBCurPtr) {         
             /* Yes            */        
       CPU_INT_EN();                         
       return;
   }

#if OS_CFG_TASK_PROFILE_EN > 0u
/* Inc. # of context switches for this new task           */
   OSTCBHighRdyPtr->CtxSwCtr++;                           
#endif
   /* Keep track of the total number of ctx switches         */
   OSTaskCtxSwCtr++;                                   

#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
   OS_TLS_TaskSw();
#endif
/* Perform interrupt level ctx switch                     */
   OSIntCtxSw();                                           
   CPU_INT_EN();
}

3.3 OS_SchedRoundRobin()

OS_SchedRoundRobin() 可能从 OSTimeTick() 或 OS_IntQTask() 中调用。

/*
***************************************************************************************
*              RUN ROUND-ROBIN SCHEDULING ALGORITHM
*
* Description: This function is called on every tick to determine if a new task at the same priority needs to execute.
*
*
* Arguments  : p_rdy_list    is a pointer to the OS_RDY_LIST entry of the ready list at the current priority
*              ----------
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

#if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u
void  OS_SchedRoundRobin (OS_RDY_LIST  *p_rdy_list)
{
    OS_TCB   *p_tcb;
    CPU_SR_ALLOC();
 /* Make sure round-robin has been enabled                 */
    if (OSSchedRoundRobinEn != DEF_TRUE) {                 
        return;
    }

    CPU_CRITICAL_ENTER();
    p_tcb = p_rdy_list->HeadPtr;                         
    if (p_tcb == (OS_TCB *)0) {
        CPU_CRITICAL_EXIT();
        return;
    }

    if (p_tcb == &OSIdleTaskTCB) {
        CPU_CRITICAL_EXIT();
        return;
    }
   /* Decrement time quanta counter                          */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {
        p_tcb->TimeQuantaCtr--;
    }
 /* Task not done with its time quanta                     */
    if (p_tcb->TimeQuantaCtr > (OS_TICK)0) {               
        CPU_CRITICAL_EXIT();
        return;
    }
 /* See if it's time to time slice current task            */
    if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) {      
  /* ... only if multiple tasks at same priority            */    
        CPU_CRITICAL_EXIT();                              
        return;
    }
/* Can't round-robin if the scheduler is locked           */
    if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        
        CPU_CRITICAL_EXIT();
        return;
    }
/* Move current OS_TCB to the end of the list             */
    OS_RdyListMoveHeadToTail(p_rdy_list);  
 /* Point to new OS_TCB at head of the list                */                 
    p_tcb = p_rdy_list->HeadPtr;         
  /* See if we need to use the default time slice           */                  
    if (p_tcb->TimeQuanta == (OS_TICK)0) {                
        p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta;
    } else {
 /* Load time slice counter with new time                  */
        p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta;          
    }
    CPU_CRITICAL_EXIT();
}
#endif

你可能感兴趣的:(ucos---Scheduling(调度))