160 likes | 286 Views
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
E N D
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
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) { /* ISRs still nested? */ return; /* only schedule when no nested ISRs */ } if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Scheduler locked? */ return; /* Yes */ } OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority ready */ OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current is still highest priority task? */ Enable interrupts /* Yes ... no need to context switch */ return; } OSTaskCtxSwCtr++; /* Increment context switch counter */ OS_TASK_SW(); /* Perform a task level context switch */ Enable interrupts }
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(); /* Perform interrupt level ctx switch */ Enable interrupts }
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) { /* Make sure round-robin has been enabled */ 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--; /* Decrement time quanta counter */ } if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* Task not done with its time quanta */ CPU_CRITICAL_EXIT(); return; } if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { /* slice only if multiple tasks at same priority */ CPU_CRITICAL_EXIT(); return; return; }
Round Robin Scheduling cont’d. if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't round-robin if the scheduler is locked */ CPU_CRITICAL_EXIT(); return; } 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