美文网首页
ucos---Scheduling(调度)

ucos---Scheduling(调度)

作者: 守拙圆 | 来源:发表于2018-09-04 19:40 被阅读112次

    调度器,也称分发器,是负责决定接下来运行哪个任务。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(调度)

          本文链接:https://www.haomeiwen.com/subject/rhbdwftx.html