μCOS-III 是一个全新的 RTOS 内核,不要将它看成 μCOS-II 的升级版。
【todo】
二、内核源代码分析
2.1 任务调度
此部分包含:(任务调度)与(就绪任务优先级、就绪任务优先级列表的实时更新维护)两部分内核功能( internal feature/services )。
代码很清晰,直接展示代码:
/* 此部分代码实现了实现了任务调度功能。Version : uC/OS-III V3.08.02 */
/*
** 1. 任务调度: 获取最高就绪优先级 和 获取最高优先级链表中需要接下来运行的任务。
** 1.1 使用前置导零的汇编指令,硬件级别支持快速查找最高优先级所在的位。
** 1.2 按照最大优先级数量,进行了三种情况的代码优化:预编译实现代码选择。
** 1.3 获取最高优先级链表中需要接下来运行的任务:
** OSSched 函数: 任务代码中调用。
** OSIntExit 函数: 中断ISR代码中调用。 OSIntEnter() and OSIntExit() 必须成对使用。
*/
/*
************************************************************************************************************************
* GET HIGHEST PRIORITY TASK WAITING
*
* Description: This function is called by other uC/OS-III services to determine the highest priority task
* waiting on the event.
*
* Arguments : none
*
* Returns : The priority of the Highest Priority Task (HPT) waiting for the event
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
OS_PRIO OS_PrioGetHighest (void)
{
#if (OS_CFG_PRIO_MAX <= (CPU_CFG_DATA_SIZE * 8u)) /* Optimize for less than word size nbr of priorities */
return ((OS_PRIO)CPU_CntLeadZeros(OSPrioTbl[0]));
#elif (OS_CFG_PRIO_MAX <= (2u * (CPU_CFG_DATA_SIZE * 8u))) /* Optimize for 2x the word size nbr of priorities */
if (OSPrioTbl[0] == 0u) {
return ((OS_PRIO)((OS_PRIO)CPU_CntLeadZeros(OSPrioTbl[1]) + (CPU_CFG_DATA_SIZE * 8u)));
} else {
return ((OS_PRIO)((OS_PRIO)CPU_CntLeadZeros(OSPrioTbl[0])));
}
#else
CPU_DATA *p_tbl;
OS_PRIO prio;
prio = 0u;
p_tbl = &OSPrioTbl[0];
while (*p_tbl == 0u) { /* Search the bitmap table for the highest priority */
prio = (OS_PRIO)(prio + (CPU_CFG_DATA_SIZE * 8u)); /* Compute the step of each CPU_DATA entry */
p_tbl++;
}
prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); /* Find the position of the first bit set at the entry */
return (prio);
#endif
}
/*
************************************************************************************************************************
* SCHEDULER
*
* Description: This function is called by other uC/OS-III services to determine whether a new, high priority task has
* been made ready to run. This function is invoked by TASK level code and is not used to reschedule tasks
* from ISRs (see OSIntExit() for ISR rescheduling).
*
* Arguments : none
*
* Returns : none
*
* Note(s) : 1) Rescheduling is prevented when the scheduler is locked (see OSSchedLock())
************************************************************************************************************************
*/
void OSSched (void)
{
CPU_SR_ALLOC();
#if (OS_CFG_INVALID_OS_CALLS_CHK_EN > 0u) /* Can't schedule when the kernel is stopped. */
if (OSRunning != OS_STATE_OS_RUNNING) {
return;
}
#endif
if (OSIntNestingCtr > 0u) { /* ISRs still nested? */
return; /* Yes ... only schedule when no nested ISRs */
}
if (OSSchedLockNestingCtr > 0u) { /* Scheduler locked? */
return; /* Yes */
}
CPU_INT_DIS();
OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority ready 获取最高就绪优先级 */
#if (OS_CFG_TASK_IDLE_EN > 0u)
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* 找到最高优先级链表中需要运行的那个任务(ready-to-run) */
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* 当前运行的任务仍然处于最高优先级? */
CPU_INT_EN(); /* Yes */
return;
}
#else // 若对 OS_CFG_TASK_IDLE_EN 数值有疑问,可参见 OS_PrioInit 函数。
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { /* 最高就绪任务优先级是不是空闲任务的优先级?(注:空闲任务的优先级只有空闲任务)*/
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* 若不是,则找到最高优先级链表中需要运行的那个任务(ready-to-run) */
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* 当前运行的任务仍然处于最高优先级? */
CPU_INT_EN(); /* Yes */
return;
}
}
#endif
OS_TRACE_TASK_PREEMPT(OSTCBCurPtr);
#if (OS_CFG_TASK_PROFILE_EN > 0u)
OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches to this task */
#endif
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
OSTaskCtxSwCtr++; /* Increment context switch counter */
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_TaskSw();
#endif
#if (OS_CFG_TASK_IDLE_EN > 0u)
OS_TASK_SW(); /* Perform a task level context switch */
CPU_INT_EN();
#else
if ((OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u))) {
OS_TASK_SW(); /* Perform a task level context switch */
CPU_INT_EN();
} else {
OSTCBHighRdyPtr = OSTCBCurPtr;
CPU_INT_EN();
for (;;) {
#if ((OS_CFG_DBG_EN > 0u) || (OS_CFG_STAT_TASK_EN > 0u))
CPU_CRITICAL_ENTER();
#if (OS_CFG_DBG_EN > 0u)
OSIdleTaskCtr++;
#endif
#if (OS_CFG_STAT_TASK_EN > 0u)
OSStatTaskCtr++;
#endif
CPU_CRITICAL_EXIT();
#endif
#if (OS_CFG_APP_HOOKS_EN > 0u)
OSIdleTaskHook(); /* Call user definable HOOK */
#endif
if ((*((volatile OS_PRIO *)&OSPrioHighRdy) != (OS_CFG_PRIO_MAX - 1u))) {
break;
}
}
}
#endif
#ifdef OS_TASK_SW_SYNC
OS_TASK_SW_SYNC();
#endif
}
/*
************************************************************************************************************************
* EXIT ISR
*
* Description: This function is used to notify uC/OS-III that you have completed servicing an ISR. When the last nested
* ISR has completed, uC/OS-III will call the scheduler to determine whether a new, high-priority task, is
* ready to run.
*
* Arguments : none
*
* Returns : none
*
* Note(s) : 1) You MUST invoke OSIntEnter() and OSIntExit() in pair. In other words, for every call to OSIntEnter()
* (or direct increment to OSIntNestingCtr) at the beginning of the ISR you MUST have a call to OSIntExit()
* at the end of the ISR.
*
* 2) Rescheduling is prevented when the scheduler is locked (see OSSchedLock())
************************************************************************************************************************
*/
void OSIntExit (void)
{
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
CPU_BOOLEAN stk_status;
#endif
CPU_SR_ALLOC();
if (OSRunning != OS_STATE_OS_RUNNING) { /* Has the OS started? */
OS_TRACE_ISR_EXIT();
return; /* No */
}
CPU_INT_DIS();
if (OSIntNestingCtr == 0u) { /* Prevent OSIntNestingCtr from wrapping */
OS_TRACE_ISR_EXIT();
CPU_INT_EN();
return;
}
OSIntNestingCtr--;
if (OSIntNestingCtr > 0u) { /* ISRs still nested? */
OS_TRACE_ISR_EXIT();
CPU_INT_EN(); /* Yes */
return;
}
if (OSSchedLockNestingCtr > 0u) { /* Scheduler still locked? */
OS_TRACE_ISR_EXIT();
CPU_INT_EN(); /* Yes */
return;
}
/* Verify ISR Stack */
#if (OS_CFG_ISR_STK_SIZE > 0u)
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
stk_status = OS_TaskStkRedzoneChk(OSCfg_ISRStkBasePtr, OSCfg_ISRStkSize);
if (stk_status != OS_TRUE) {
OSRedzoneHitHook((OS_TCB *)0);
}
#endif
#endif
OSPrioHighRdy = OS_PrioGetHighest(); /* Find highest priority 获取最高就绪优先级 */
#if (OS_CFG_TASK_IDLE_EN > 0u)
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* 找到最高优先级链表中需要运行的那个任务(ready-to-run) */
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
/* Yes */
#if (OS_CFG_TASK_STK_REDZONE_EN > 0u)
stk_status = OSTaskStkRedzoneChk((OS_TCB *)0);
if (stk_status != OS_TRUE) {
OSRedzoneHitHook(OSTCBCurPtr);
}
#endif
OS_TRACE_ISR_EXIT();
CPU_INT_EN();
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); /* Do this here because we don't execute OSIntCtxSw(). */
return;
}
#else
if (OSPrioHighRdy != (OS_CFG_PRIO_MAX - 1u)) { /* Are we returning to idle? */
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* No ... get highest priority task ready-to-run */
if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */
/* Yes */
OS_TRACE_ISR_EXIT();
CPU_INT_EN();
OS_TRACE_TASK_SWITCHED_IN(OSTCBHighRdyPtr); /* Do this here because we don't execute OSIntCtxSw(). */
return;
}
}
#endif
#if (OS_CFG_TASK_PROFILE_EN > 0u)
OSTCBHighRdyPtr->CtxSwCtr++; /* Inc. # of context switches for this new task */
#endif
#if ((OS_CFG_TASK_PROFILE_EN > 0u) || (OS_CFG_DBG_EN > 0u))
OSTaskCtxSwCtr++; /* Keep track of the total number of ctx switches */
#endif
#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)
OS_TLS_TaskSw();
#endif
OS_TRACE_ISR_EXIT_TO_SCHEDULER();
OSIntCtxSw(); /* Perform interrupt level ctx switch */
CPU_INT_EN();
}
/*
** 2. ucosiii内核对任务优先级以及优先级对应就绪任务列表的实时更新维护。
** 2.1 对优先级的实时更新维护: OS_PrioInsert 函数 和 OS_PrioRemove 函数。
** 2.2 对就绪任务优先级对应的链表,即任务就绪列表,的实时更新维护: OS_RdyListInsert 函数 和 OS_RdyListRemove 函数。
*/
/*
************************************************************************************************************************
* INSERT PRIORITY
*
* Description: This function is called to insert a priority in the priority table.
*
* Arguments : prio is the priority to insert
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
void OS_PrioInsert (OS_PRIO prio)
{
#if (OS_CFG_PRIO_MAX <= (CPU_CFG_DATA_SIZE * 8u)) /* Optimize for less than word size nbr of priorities */
OSPrioTbl[0] |= (CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - prio);
#elif (OS_CFG_PRIO_MAX <= (2u * (CPU_CFG_DATA_SIZE * 8u))) /* Optimize for 2x the word size nbr of priorities */
if (prio < (CPU_CFG_DATA_SIZE * 8u)) {
OSPrioTbl[0] |= (CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - prio);
} else {
OSPrioTbl[1] |= (CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - (prio - (CPU_CFG_DATA_SIZE * 8u)));
}
#else
CPU_DATA bit_nbr;
OS_PRIO ix;
ix = (OS_PRIO)(prio / (CPU_CFG_DATA_SIZE * 8u));
bit_nbr = (CPU_DATA)prio & ((CPU_CFG_DATA_SIZE * 8u) - 1u);
OSPrioTbl[ix] |= (CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - bit_nbr);
#endif
}
/*
************************************************************************************************************************
* REMOVE PRIORITY
*
* Description: This function is called to remove a priority in the priority table.
*
* Arguments : prio is the priority to remove
*
* Returns : none
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
void OS_PrioRemove (OS_PRIO prio)
{
#if (OS_CFG_PRIO_MAX <= (CPU_CFG_DATA_SIZE * 8u)) /* Optimize for less than word size nbr of priorities */
OSPrioTbl[0] &= ~((CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - prio));
#elif (OS_CFG_PRIO_MAX <= (2u * (CPU_CFG_DATA_SIZE * 8u))) /* Optimize for 2x the word size nbr of priorities */
if (prio < (CPU_CFG_DATA_SIZE * 8u)) {
OSPrioTbl[0] &= ~((CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - prio));
} else {
OSPrioTbl[1] &= ~((CPU_DATA)1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - (prio - (CPU_CFG_DATA_SIZE * 8u))));
}
#else
CPU_DATA bit_nbr;
OS_PRIO ix;
ix = (OS_PRIO)(prio / (CPU_CFG_DATA_SIZE * 8u));
bit_nbr = (CPU_DATA)prio & ((CPU_CFG_DATA_SIZE * 8u) - 1u);
OSPrioTbl[ix] &= ~((CPU_DATA) 1u << (((CPU_CFG_DATA_SIZE * 8u) - 1u) - bit_nbr));
#endif
}
/*
************************************************************************************************************************
* INSERT TCB IN THE READY LIST
*
* Description: This function is called to insert a TCB in the ready list.
*
* The TCB is inserted at the tail of the list if the priority of the TCB is the same as the priority of the
* current task. The TCB is inserted at the head of the list if not.
*
* Arguments : p_tcb is a pointer to the TCB to insert into the ready list
* -----
*
* Returns : none
*
* Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/
void OS_RdyListInsert (OS_TCB *p_tcb)
{
OS_PrioInsert(p_tcb->Prio);
if (p_tcb->Prio == OSPrioCur) { /* Are we readying a task at the same prio? */
OS_RdyListInsertTail(p_tcb); /* Yes, insert readied task at the end of the list */
} else {
OS_RdyListInsertHead(p_tcb); /* No, insert readied task at the beginning of the list*/
}
OS_TRACE_TASK_READY(p_tcb);
}
/*
************************************************************************************************************************
* REMOVE TCB FROM LIST KNOWING ONLY WHICH OS_TCB TO REMOVE
*
* Description: This function is called to remove an OS_TCB from an OS_RDY_LIST knowing the address of the OS_TCB to
* remove.
*
*
* CASE 0: TCB list is empty, nothing to do.
*
* CASE 1: Only 1 OS_TCBs in the list.
*
* OS_RDY_LIST
* +--------------+ OS_TCB
* | TailPtr |--+---> +------------+
* +--------------+ | | NextPtr |->0
* | HeadPtr |--/ +------------+
* +--------------+ 0<-| PrevPtr |
* | NbrEntries=1 | +------------+
* +--------------+ : :
* : :
* +------------+
*
* CASE N: Two or more OS_TCBs in the list.
*
* OS_RDY_LIST
* +--------------+
* | TailPtr |-----------------------------------------------+
* +--------------+ OS_TCB OS_TCB | OS_TCB
* | HeadPtr |------> +------------+ +------------+ +-> +------------+
* +--------------+ | NextPtr |------>| NextPtr | ...... | NextPtr |->0
* | NbrEntries=N | +------------+ +------------+ +------------+
* +--------------+ 0<-| PrevPtr |<------| PrevPtr | ...... | PrevPtr |
* +------------+ +------------+ +------------+
* : : : : : :
* : : : : : :
* +------------+ +------------+ +------------+
*
*
* Arguments : p_tcb is a pointer to the OS_TCB to remove
* -----
*
* Returns : A pointer to the OS_RDY_LIST where the OS_TCB was
*
* Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/
void OS_RdyListRemove (OS_TCB *p_tcb)
{
OS_RDY_LIST *p_rdy_list;
OS_TCB *p_tcb1;
OS_TCB *p_tcb2;
p_rdy_list = &OSRdyList[p_tcb->Prio];
p_tcb1 = p_tcb->PrevPtr; /* Point to next and previous OS_TCB in the list */
p_tcb2 = p_tcb->NextPtr;
if (p_tcb1 == (OS_TCB *)0) { /* Was the OS_TCB to remove at the head? */
if (p_tcb2 == (OS_TCB *)0) { /* Yes, was it the only OS_TCB? */
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries = 0u; /* Yes, no more entries */
#endif
p_rdy_list->HeadPtr = (OS_TCB *)0;
p_rdy_list->TailPtr = (OS_TCB *)0;
OS_PrioRemove(p_tcb->Prio);
} else {
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries--; /* No, one less entry */
#endif
p_tcb2->PrevPtr = (OS_TCB *)0; /* adjust back link of new list head */
p_rdy_list->HeadPtr = p_tcb2; /* adjust OS_RDY_LIST's new head */
}
} else {
#if (OS_CFG_DBG_EN > 0u)
p_rdy_list->NbrEntries--; /* No, one less entry */
#endif
p_tcb1->NextPtr = p_tcb2;
if (p_tcb2 == (OS_TCB *)0) {
p_rdy_list->TailPtr = p_tcb1; /* Removing the TCB at the tail, adj the tail ptr */
} else {
p_tcb2->PrevPtr = p_tcb1;
}
}
p_tcb->PrevPtr = (OS_TCB *)0;
p_tcb->NextPtr = (OS_TCB *)0;
OS_TRACE_TASK_SUSPENDED(p_tcb);
}