http://blog.csdn.net/suiyuan19840208/article/details/15340747 2013
1:任务就绪表
操作系统为就绪的任务分配CPU的使用权是操作系统的核心工作,操作系统必要能够判断系统的
那些任务是出于就绪状态,同时对就绪的任务进行调度执行。
在任务的各种状态中,OS总是从就绪的任务列表选取将要运行的任务。在ucos中,系统使用二进制的位图来记录
系统中处于就绪状态的任务,用二进制的0和1来表示此任务是否出于就绪状态。
在ucos中定义全局变量OSRdyTbl来表示任务就绪表
#if OS_LOWEST_PRIO <= 63
OS_EXT INT8U OSRdyGrp; /* Ready list group */
OS_EXT INT8U OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */
#else
OS_EXT INT16U OSRdyGrp; /* Ready list group */
OS_EXT INT16U OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */
#endif
其中OS_RDY_TBL_SIZE定义为:
#if OS_LOWEST_PRIO <= 63
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 8 + 1) /* Size of event table */
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8 + 1) /* Size of ready table */
#else
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 16 + 1) /* Size of event table */
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 16 + 1) /* Size of ready table */
#endif
当OS_LOWEST_PRIO <= 63时,任务就绪表对大能表示的二进制位数为32(4*8),则表示ucos最多能记录出于就绪状态的任务数。
其中OSRdyTbl定义为INT8U,上面的每一个bit表示此优先级的对应的任务的就绪状态,当为1时表示就绪,当为0时此任务非就绪。任务就绪表中的每一个元素中的bit位的编号代表任务的优先级。就绪表中的一个元素可以表示8个任务的就绪状态。因此就绪表中的8个元素就可以表示64(8*8)个任务的就绪状态,将就绪表中的每一个元素所代表的8个任务视为一个任务组。在上面的代码中定义了:OSRdyGrp,OSRdyGrp中的每一个bit代表了处于就绪状态的任务组。
上面的表对应的是当前系统中任务的个数为64的情况,OSRdyGrp中的那个bit为1,则表示此位对应的就绪表元素中
有出于就绪状态的任务。
上面的文字说明的是如何根据一个任务的优先级来确定在就绪表中的位置,现在看看相关的代码。
在任务的TCB中定义了所谓的X和Y变量,从上面的代码中可以知道,Y:代表的是在哪一个就绪表中元素中,X:代表的是在就绪表元素Y中的哪一个bit上。其中OSRdyGrp与Y进行与操作将告诉组中的哪一个元素组中有任务处于就绪状态。有了X和Y的值,就可以在任务就绪表中取得最高优先级的任务ID标识。在ucos中使用以下方式:
2: 任务调度
从字面意思是就是调度,但是调度什么了?在多任务的操作系统中就是调度任务。在多任务系统中,令CPU终止当前正在运行的任务转而去运行另一个任务的工作叫任务切换,而按某种规则进行切换的工作叫任务的调度。
在ucos中任务调度由任务调度器来完成,任务的调度的主要工作有:
1:在任务就绪表中取出优先级最高的就绪任务。
2:实现任务的切换。
在ucos中有俩种类型的调度器:1:任务级的调度器,2:中断级的调度器。任务级的调度由函数:OS_Sched (void)来实现。中断级的调度由:OSIntExit (void)来实现。
在ucos中对任务的管理是通过任务控制块来完成,因此在进行任务切换之前就必须获取到当前就绪及正在运行的任务的TCB,
在前面看到过全局变量:
OS_EXT OS_TCB *OSTCBCur; /* Pointer to currently running TCB */
OS_EXT OS_TCB *OSTCBHighRdy; /* Pointer to highest priority TCB R-to-R */
OS_EXT INT8U OSPrioCur; /* Priority of current task */
OS_EXT INT8U OSPrioHighRdy; /* Priority of highest priority task */
在任务调度器函数中:判断当前任务的优先级ID与就绪任务中的优先级ID是否不等,如果不等的话,则进行任务的切换。之后根据优先级ID在全局的OSTCBPrioTbl任务TCB中取出对于此ID的任务TCB,
即OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
/*
*********************************************************************************************************
* SCHEDULER
*
* Description: This function is called by other uC/OS-II 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
*
* Notes : 1) This function is INTERNAL to uC/OS-II and your application should not call it.
* 2) Rescheduling is prevented when the scheduler is locked (see OS_SchedLock())
*********************************************************************************************************
*/
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0) { /* ... scheduler is not locked */
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
在上面的代码中OS_SchedNew()函数就是在就绪任务中找出优先级最高的任务的ID即OSPrioHighRdy的值是最高优先级任务的优先级ID。
/*
*********************************************************************************************************
* FIND HIGHEST PRIORITY TASK READY TO RUN
*
* Description: This function is called by other uC/OS-II services to determine the highest priority task
* that is ready to run. The global variable 'OSPrioHighRdy' is changed accordingly.
*
* Arguments : none
*
* Returns : none
*
* Notes : 1) This function is INTERNAL to uC/OS-II and your application should not call it.
* 2) Interrupts are assumed to be disabled when this function is called.
*********************************************************************************************************
*/
static void OS_SchedNew(void)
{
#if OS_LOWEST_PRIO <= 63 /* See if we support up to 64 tasks */
INT8U y;
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
#else /* We support up to 256 tasks */
INT8U y;
INT16U *ptbl;
if ((OSRdyGrp & 0xFF) != 0) {
y = OSUnMapTbl[OSRdyGrp & 0xFF];
} else {
y = OSUnMapTbl[(OSRdyGrp >> 8) & 0xFF] + 8;
}
ptbl = &OSRdyTbl[y];
if ((*ptbl & 0xFF) != 0) {
OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl & 0xFF)]);
} else {
OSPrioHighRdy = (INT8U)((y << 4) + OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8);
}
#endif
}
3:任务切换
上面的函数OS_Sched()为任务级的任务切换函数,在函数当(OSPrioHighRdy != OSPrioCur)的时候,将会执行
OS_TASK_SW(); /* Perform a context switch */
其中任务切换就是:种植当前在整在运行的任务(OSTCBCur),转而执行另一个任务(OSTCBHighRdy)的过程.
#define OS_TASK_SW() OSCtxSw()
其中OSCtxSw为汇编代码。
在cortex-M3中有一个系统时钟tick,周期性的为系统提供系统时钟。
是一个中断处理函数,周期性的执行,在其代码中有函数。void OSIntExit (void),其函数内容和OS_SchedNew
一致的。
void OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0;
#endif
if (OSRunning == OS_TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
if (OSIntNesting == 0) { /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0) { /* ... and not locked. */
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
#endif
OSCtxSwCtr++; /* Keep track of the number of ctx switches */
OSIntCtxSw(); /* Perform interrupt level ctx switch */
}
}
}
OS_EXIT_CRITICAL();
}
}
;********************************************************************************************************
; PERFORM A CONTEXT SWITCH (From task level)
; void OSCtxSw(void)
;
; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function
; triggers the PendSV exception which is where the real work is done.
;********************************************************************************************************
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
从上面的解析可以知道,上面的代码触发 PendSV exception异常,其PendSV中断处理函数才是实际的任务切换工作。
;********************************************************************************************************
; HANDLE PendSV EXCEPTION
; void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the
; processor context on any exception, and restores same on return from exception. So only
; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception
; this way means that context saving and restoring is identical whether it is initiated from
; a thread or occurs due to an interrupt or exception.
;
; 2) Pseudo-code is:
; a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);
; b) Save remaining regs r4-r11 on process stack;
; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;
; d) Call OSTaskSwHook();
; e) Get current high priority, OSPrioCur = OSPrioHighRdy;
; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;
; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;
; h) Restore R4-R11 from new process stack;
; i) Perform exception return which will restore remaining context.
;
; 3) On entry into PendSV handler:
; a) The following have been saved on the process stack (by processor):
; xPSR, PC, LR, R12, R0-R3
; b) Processor mode is switched to Handler mode (from Thread mode)
; c) Stack is Main stack (switched from Process stack)
; d) OSTCBCur points to the OS_TCB of the task to suspend
; OSTCBHighRdy points to the OS_TCB of the task to resume
;
; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
; know that it will only be run when no other exception or interrupt is active, and
; therefore safe to assume that context being switched out was using the process stack (PSP).
;********************************************************************************************************
OS_CPU_PendSVHandler
CPSID I ; Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, OS_CPU_PendSVHandler_nosave ; Skip register save the first time
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
STM R0, {R4-R11}
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
PUSH {R14} ; Save LR exc_return value
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; Restore r4-11 from new process stack
ADDS R0, R0, #0x20
MSR PSP, R0 ; Load PSP with new process SP
ORR LR, LR, #0x04 ; Ensure exception return uses process stack
CPSIE I
BX LR ; Exception return will restore remaining context
END
|