Scheduling in µC/OS-III Akos Ledeczi EECE 354, Fall 2012 Vanderbilt University Ready “List” • Not a simple linked list of tasks ordered by priority • Priority bitmap: – Helps quickly identify the highest priority level that has at least one task ready to run – Count Leading Zeros (CLZ) instruction in many CPUs • Actual list is an array of ready lists: one for each priority level Ready “List” OS Tasks: Empty list: Ready “List” • Adding a task always puts it at the end of the list Preemptive Scheduling: Direct Post Preemptive Scheduling: Deferred Post Scheduling Points • • • • • • • • • • • • • • Post (unless call made with OS_OPT_POST_NO_SCHED) Call delay Pend (unless event has already occurred) Abort pend (only by another task of course) Task creation Task deletion Kernel object deletion (tasks pending on these become ready) Priority change Suspend Resume (only by another task of course) End of all nested ISRs (OSIntExit() instead of OSSched()) Scheduler unlock (only final unlock as locking the scheduler can be nested) Yield in round robin scheduling Explicitly calling the scheduler Round Robin Scheduling • • • • • Time Slicing Must call OSSchedRoundRobinCfg() to enable round robin scheduling Yield Time quanta can be set on a per task basis Time quanta can be changed at run-time Scheduling from Task Level void OSSched (void) { Disable interrupts if (OSIntNestingCtr > (OS_NESTING_CTR)0) { return; } } /* ISRs still nested? /* only schedule when no nested ISRs */ */ if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { return; } OSPrioHighRdy = OS_PrioGetHighest(); OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; if (OSTCBHighRdyPtr == OSTCBCurPtr) { Enable interrupts return; } OSTaskCtxSwCtr++; /* Scheduler locked? /* Yes */ */ /* Find the highest priority ready */ /* Increment context switch counter */ OS_TASK_SW(); Enable interrupts /* Perform a task level context switch */ /* Current is still highest priority task? */ /* Yes ... no need to context switch */ Scheduling from Interrupt Level void OSIntExit (void) { Disable interrupts if (OSIntNestingCtr == (OS_NESTING_CTR)0) { /* Prevent OSIntNestingCtr from wrapping return; } OSIntNestingCtr--; if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* ISRs still nested? Enable interrupts /* Yes return; } if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Scheduler still locked? Enable interrupts /* Yes return; } OSPrioHighRdy = OS_PrioGetHighest(); /* Find highest priority OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* Get highest priority task ready-to-run if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? Enable interrupts /* Yes return; } OSTaskCtxSwCtr++; /* Keep track of the total number of ctx switches OSIntCtxSw(); Enable interrupts } /* Perform interrupt level ctx switch */ */ */ */ */ */ */ */ */ */ */ Round Robin Scheduling #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list) { OS_TCB *p_tcb; 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; } if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { p_tcb->TimeQuantaCtr--; } if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { CPU_CRITICAL_EXIT(); return; } if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { CPU_CRITICAL_EXIT(); return; return; } /* Make sure round-robin has been enabled */ /* Decrement time quanta counter */ /* Task not done with its time quanta */ /* slice only if multiple tasks at same priority */ Round Robin Scheduling cont’d. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { CPU_CRITICAL_EXIT(); return; } /* Can't round-robin if the scheduler is locked OS_RdyListMoveHeadToTail(p_rdy_list); /* Move current OS_TCB to the end of the list p_tcb = p_rdy_list->HeadPtr; /* Point to new OS_TCB at head of the list if (p_tcb->TimeQuanta == (OS_TICK)0) { /* See if we need to use the default time slice p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta; } else { p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time } CPU_CRITICAL_EXIT(); } #endif */ */ */ */ */ Task Level Context Switch: Before Must prepare stack as if an interrupt occurred Task Level Context Switch: After Registers are restored from the new task’s stack It is a “simulated” return from interrupt ISR Level Context Switch: Before Interrupt has already caused registers to be saved on the stack ISR Level Context Switch: After