uC/ OS- II源码分析
在这里我不想分模块来慢慢分析这个OS的行为,我们从CPU的执行顺序来看吧,这样也许清晰一些,并且我们暂时抛弃那些Event/ MailBox、信号量等元素,还是先看看作为一个OS核心的Task Schedule部分内容吧。
首先从main函数开始,下面是uC/ OS- II main函数的大致流程:
main( ) {
OSInit( ) ;
TaskCreate( . . . ) ;
OSStart( ) ;
}
首先是调用OSInit进行初始化,然后使用TaskCreate创建几个进程/ Task,最后调用OSStart,操作系统就开始运行了。
OSInit
最先看看OSInit完成哪些初始化:
void OSInit ( void )
{
# if OS_VERSION > = 204
OSInitHookBegin( ) ; /* Call port specific initialization code */
# endif
OS_InitMisc( ) ; /* Initialize miscellaneous variables */
OS_InitRdyList( ) ; /* Initialize the Ready List */
OS_InitTCBList( ) ; /* Initialize the free list of OS_TCBs */
OS_InitEventList( ) ; /* Initialize the free list of OS_EVENTs */
# if ( OS_VERSION > = 251) & & ( OS_FLAG_EN > 0) & & ( OS_MAX_FLAGS > 0)
OS_FlagInit( ) ; /* Initialize the event flag structures */
# endif
# if ( OS_MEM_EN > 0) & & ( OS_MAX_MEM_PART > 0)
OS_MemInit( ) ; /* Initialize the memory manager */
# endif
# if ( OS_Q_EN > 0) & & ( OS_MAX_QS > 0)
OS_QInit( ) ; /* Initialize the message queue structures */
# endif
OS_InitTaskIdle( ) ; /* Create the Idle Task */
# if OS_TASK_STAT_EN > 0
OS_InitTaskStat( ) ; /* Create the Statistic Task */
# endif
# if OS_VERSION > = 204
OSInitHookEnd( ) ; /* Call port specific init. code */
# endif
# if OS_VERSION > = 270 & & OS_DEBUG_EN > 0
OSDebugInit( ) ;
# endif
}
OS_InitMisc( ) 完成的是一些其其他他的变量的初始化:
OSIntNesting = 0; /* Clear the interrupt nesting counter */
OSLockNesting = 0; /* Clear the scheduling lock counter */
OSTaskCtr = 0; /* Clear the number of tasks */
OSRunning = FALSE ; /* Indicate that multitasking not started */
OSCtxSwCtr = 0; /* Clear the context switch counter */
OSIdleCtr = 0L; /* Clear the 32-bit idle counter */
其中包括:中断嵌套标志OSIntNesting,调度锁定标志OSLockNesting,OS标志OSRunning等。OSRunning在这里设置为FALSE,在后面OSStartHighRdy中会被设置为TRUE表示OS开始工作。
OS_InitRdyList( ) 初始化就绪Task列表:
static void OS_InitRdyList ( void )
{
INT8U i;
INT8U * prdytbl;
OSRdyGrp = 0x00; /* Clear the ready list */
prdytbl = & OSRdyTbl[ 0] ;
for ( i = 0; i < OS_RDY_TBL_SIZE; i+ + ) {
* prdytbl+ + = 0x00;
}
OSPrioCur = 0;
OSPrioHighRdy = 0;
OSTCBHighRdy = ( OS_TCB * ) 0;
OSTCBCur = ( OS_TCB * ) 0;
}
首先将OSRdyTbl[ ] 数组中全部初始化0,同时将OSPrioCur/ OSTCBCur初始化为0,OSPrioHighRdy/ OSTCBHighRdy也初始化为0,这几个变量将在第一个OSSchedule中被赋予正确的值。
OS_InitTCBList( ) 这个函数看名称我们就知道是初始化TCB列表。
static void OS_InitTCBList ( void )
{
INT8U i;
OS_TCB * ptcb1;
OS_TCB * ptcb2;
OS_MemClr( ( INT8U * ) & OSTCBTbl[ 0] , sizeof ( OSTCBTbl) ) ; /* Clear all the TCBs */
OS_MemClr( ( INT8U * ) & OSTCBPrioTbl[ 0] , sizeof ( OSTCBPrioTbl) ) ; /* Clear the priority table */
ptcb1 = & OSTCBTbl[ 0] ;
ptcb2 = & OSTCBTbl[ 1] ;
for ( i = 0; i < ( OS_MAX_TASKS + OS_N_SYS_TASKS - 1) ; i+ + ) { /* Init. list of free TCBs */
ptcb1- > OSTCBNext = ptcb2;
# if OS_TASK_NAME_SIZE > 1
ptcb1- > OSTCBTaskName[ 0] = '?' ; /* Unknown name */
ptcb1- > OSTCBTaskName[ 1] = OS_ASCII_NUL;
# endif
ptcb1+ + ;
ptcb2+ + ;
}
ptcb1- > OSTCBNext = ( OS_TCB * ) 0; /* Last OS_TCB */
# if OS_TASK_NAME_SIZE > 1
ptcb1- > OSTCBTaskName[ 0] = '?' ; /* Unknown name */
ptcb1- > OSTCBTaskName[ 1] = OS_ASCII_NUL;
# endif
OSTCBList = ( OS_TCB * ) 0; /* TCB lists initializations */
OSTCBFreeList = & OSTCBTbl[ 0] ;
}
这里完成的工作很简单,首先把整个数组使用OSTCBNext指针连接成链表链起来,然后将OSTCBList初始化为0,也就是还没有TCB,因为还没有Task产生,OSTCBFreeList指向OSTCBTbl[ ] 数组的第一个表示所有TCB都处于Free状态。
OS_InitEventList( ) 初始化Event列表。
static void OS_InitEventList ( void )
{
# if OS_EVENT_EN & & ( OS_MAX_EVENTS > 0)
# if ( OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT * pevent1;
OS_EVENT * pevent2;
OS_MemClr( ( INT8U * ) & OSEventTbl[ 0] , sizeof ( OSEventTbl) ) ; /* Clear the event table */
pevent1 = & OSEventTbl[ 0] ;
pevent2 = & OSEventTbl[ 1] ;
for ( i = 0; i < ( OS_MAX_EVENTS - 1) ; i+ + ) { /* Init. list of free EVENT control blocks */
pevent1- > OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1- > OSEventPtr = pevent2;
# if OS_EVENT_NAME_SIZE > 1
pevent1- > OSEventName[ 0] = '?' ; /* Unknown name */
pevent1- > OSEventName[ 1] = OS_ASCII_NUL;
# endif
pevent1+ + ;
pevent2+ + ;
}
pevent1- > OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1- > OSEventPtr = ( OS_EVENT * ) 0;
# if OS_EVENT_NAME_SIZE > 1
pevent1- > OSEventName[ 0] = '?' ;
pevent1- > OSEventName[ 1] = OS_ASCII_NUL;
# endif
OSEventFreeList = & OSEventTbl[ 0] ;
# else
OSEventFreeList = & OSEventTbl[ 0] ; /* Only have ONE event control block */
OSEventFreeList- > OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList- > OSEventPtr = ( OS_EVENT * ) 0;
# if OS_EVENT_NAME_SIZE > 1
OSEventFreeList- > OSEventName[ 0] = '?' ; /* Unknown name */
OSEventFreeList- > OSEventName[ 1] = OS_ASCII_NUL;
# endif
# endif
# endif
}
同样将EventTbl[ ] 数组中的OSEventType都初始化为OS_EVENT_TYPE_UNUSED。
OS_InitTaskIdle( ) ,中间我们跳过其他的如Mem等的初始化,看看Idle Task的初始化。
( void ) OSTaskCreateExt( OS_TaskIdle,
( void * ) 0, /* No arguments passed to OS_TaskIdle() */
& OSTaskIdleStk[ OS_TASK_IDLE_STK_SIZE - 1] , /* Set Top-Of-Stack */
OS_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
& OSTaskIdleStk[ 0] , /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
( void * ) 0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR) ; /* Enable stack checking + clear stack */
其实Idle Task的初始化很简单就是调用OSTaskCrete系列的函数创建一个Task, OSTaskCreate我们后面再做进一步分析。
初始化State Task也是类似调用OSTaskCreate系列函数创建Stat Task。这里只是创建了该Task的各个结构还没有真正运行该Task,直到OSStart中才依据优先级调度运行。
OK,到这里OSInit算高一个段落了,我们接着回到main往下看。
OSTaskCreate
OSTaskCreate负责创建Task所需的数据结构,该函数原形如下所示:
INT8U OSTaskCreate ( void ( * task) ( void * pd) , void * p_arg, OS_STK * ptos, INT8U prio)
其中task是一个函数指针,指向该Task所开始的函数,当这个Task第一次被调度运行时将会从task处开始运行。
p_arg是传给task的参数指针;
ptos是堆栈指针,指向栈顶(堆栈从上往下)或栈底(堆栈从下往上);
prio是进程的优先级,uC/ OS- II共支持最大64个优先级,其中最低的两个优先级给Idle和Stat进程,并且各个Task的优先级必须不同。
接下来,我们看看这个函数的执行流程:
# if OS_ARG_CHK_EN > 0
if ( prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return ( OS_PRIO_INVALID) ;
}
# endif
OS_ENTER_CRITICAL( ) ;
if ( OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL( ) ;
return ( OS_ERR_TASK_CREATE_ISR) ;
}
if ( OSTCBPrioTbl[ prio] = = ( OS_TCB * ) 0) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[ prio] = ( OS_TCB * ) 1; /* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL( ) ;
psp = ( OS_STK * ) OSTaskStkInit( task, p_arg, ptos, 0) ; /* Initialize the task's stack */
err = OS_TCBInit( prio, psp, ( OS_STK * ) 0, 0, 0, ( void * ) 0, 0) ;
if ( err = = OS_NO_ERR) {
if ( OSRunning = = TRUE ) { /* Find highest priority task if multitasking has started */
OS_Sched( ) ;
}
} else {
OS_ENTER_CRITICAL( ) ;
OSTCBPrioTbl[ prio] = ( OS_TCB * ) 0; /* Make this priority available to others */
OS_EXIT_CRITICAL( ) ;
}
return ( err) ;
}
OS_EXIT_CRITICAL( ) ;
return ( OS_PRIO_EXIST) ;
OS_LOWEST_PRIO在ucos- ii. h中被定义为63,表示Task的优先级从0到63,共64级。首先判断prio是否超过最低优先级,如果是,则返回OS_PRIO_INVALID错误。
然后调用OS_ENTER_CRITICAL( ) ,进入临界段,在临界段中的代码执行不允许被中断。这个宏是用户自定义的,一般是进行关中断操作,例如在x86中的CLI等。这个宏和OS_EXIT_CRITICAL( ) 相对应,这个宏表示离开临界段。
OSTaskCreate不允许在中断中调用,因此会判断OSIntNesting是否大于0,如果大于0,表示正在中断嵌套,返回OS_ERR_TASK_CREATE_ISR错误。
接着判断该prio是否已经有Task存在,由于uC/ OS- II只支持每一个优先级一个Task,因此如果该prio已经有进程存在,OSTaskCreate会返回OS_PRIO_EXIST错误。
相反,如果该prio先前没有Task存在,则将OSTCBPrioTbl[ prio] 置1,表示该prio已被占用,然后调用OSTaskStkInit初始化堆栈,调用OS_TCBInit初始化TCB,如果OSRunning为TRUE表示OS正在运行,则调用OS_Sched进行进程调度;否则返回。
下面来看看OSTaskStkInit和OS_TCBInit这两个函数。
OSTaskStkInit是一个用户自定义的函数,因为uC/ OS- II在设计时无法知道当前处理器在进行进程调度时需要保存那些信息,OSTaskStkInit就是初始化堆栈,让Task看起来就好像刚刚进入中断并保存好寄存器的值一样,当OS_Sched调度到该Task时,只需切换到该堆栈中,将寄存器值Pop出来,然后执行一个中断返回指令IRET即可。
OSTaskStkInit的原型如下:
OS_STK * OSTaskStkInit ( void ( * task) ( void * pd) , void * pdata, OS_STK * ptos, INT16U opt)
和OSTaskCreate类似,task是进程入口地址,pdata是参数地址,ptos是堆栈指针,而opt只是作为一个预留的参数Option而保留。返回的是调整以后的堆栈指针。
在OSTaskStkInit中,一般是将pdata入栈,flag入栈,task入栈,然后将各寄存器依次入栈。
OS_TCBInit初始化TCB数据结构,下面只提取主要部分来看:
INT8U OS_TCBInit ( INT8U prio, OS_STK * ptos, OS_STK * pbos, INT16U id, INT32U stk_size, void * pext, INT16U opt)
{
OS_TCB * ptcb;
OS_ENTER_CRITICAL( ) ;
ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */
if ( ptcb ! = ( OS_TCB * ) 0) {
OSTCBFreeList = ptcb- > OSTCBNext; /* Update pointer to free TCB list */
OS_EXIT_CRITICAL( ) ;
ptcb- > OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */
ptcb- > OSTCBPrio = prio; /* Load task priority into TCB */
ptcb- > OSTCBStat = OS_STAT_RDY; /* Task is ready to run */
ptcb- > OSTCBPendTO = FALSE ; /* Clear the Pend timeout flag */
ptcb- > OSTCBDly = 0; /* Task is not delayed */
# if OS_TASK_CREATE_EXT_EN > 0
ptcb- > OSTCBExtPtr = pext; /* Store pointer to TCB extension */
ptcb- > OSTCBStkSize = stk_size; /* Store stack size */
ptcb- > OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
ptcb- > OSTCBOpt = opt; /* Store task options */
ptcb- > OSTCBId = id; /* Store task ID */
# else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
# endif
# if OS_TASK_DEL_EN > 0
ptcb- > OSTCBDelReq = OS_NO_ERR;
# endif
ptcb- > OSTCBY = ( INT8U) ( prio > > 3) ; /* Pre-compute X, Y, BitX and BitY */
ptcb- > OSTCBBitY = OSMapTbl[ ptcb- > OSTCBY] ;
ptcb- > OSTCBX = ( INT8U) ( prio & 0x07) ;
ptcb- > OSTCBBitX = OSMapTbl[ ptcb- > OSTCBX] ;
# if OS_EVENT_EN
ptcb- > OSTCBEventPtr = ( OS_EVENT * ) 0; /* Task is not pending on an event */
# endif
OSTaskCreateHook( ptcb) ; /* Call user defined hook */
OS_ENTER_CRITICAL( ) ;
OSTCBPrioTbl[ prio] = ptcb;
ptcb- > OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb- > OSTCBPrev = ( OS_TCB * ) 0;
if ( OSTCBList ! = ( OS_TCB * ) 0) {
OSTCBList- > OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp | = ptcb- > OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ ptcb- > OSTCBY] | = ptcb- > OSTCBBitX;
OSTaskCtr+ + ; /* Increment the #tasks counter */
OS_EXIT_CRITICAL( ) ;
return ( OS_NO_ERR) ;
}
OS_EXIT_CRITICAL( ) ;
return ( OS_NO_MORE_TCB) ;
}
首先调用OS_ENTER_CRITICAL进入临界段,首先从OSTCBFreeList中拿出一个TCB,如果OSTCBFreeList为空,则返回OS_NO_MORE_TCB错误。
然后调用OS_EXIT_CRITICAL离开临界段,接着对该TCB进行初始化:
将OSTCBStkPtr初始化为该Task当前堆栈指针;
OSTCBPrio设置为该Task的prio;
OSTCBStat设置为OS_STAT_RDY,表示就绪状态;
OSTCBDly设置为0,当该Task调用OSTimeDly时会初始化这个变量为Delay的时钟数,然后Task转入OS_STAT_状态。这个变量在OSTimeTick中检查,如果大于0表示还需要进行Delay,则进行减1;如果等于零表示无须进行Delay,可以马上运行,转入OS_STAT_RDY状态。
OSTCBBitY和OSTCBBitX的作用我们在等会专门来讨论。
紧接着就要将该TCB插入OSTCBList列表中,先调用OS_ENTER_CRITICAL进入临界段,将该TCB插入到OSTCBList成为第一个节点,然后调整OSRdyGrp和OSRdyTbl,(这两个变量一会和OSTCBBitX/ OSTCBBitY一起讨论),最后将OSTaskCtr计数器加一,调用OS_EXIT_CRITICAL退出临界段。
OSMapTbl和OSUnMapTbl
刚才我们看到TCB数据结构中的OSTCBBitX/ OSTCBBitY以及OSRdyGrp/ OSRdyTbl的使用,这里专门来讨论讨论这几个变量的用法。
uC/ OS- II将64个优先级的进程分为8组,每组8个。刚好可以使用8个INT8U的数据进行表示,于是这就是OSRdyGrp和OSRdyTbl的由来,OSRdyGrp表示组别,从0到7,从前面我们可以看到OSRdyGrp和OSRdyTbl是这么被赋值的:
OSRdyGrp | = ptcb- > OSTCBBitY;
OSRdyTbl[ ptcb- > OSTCBY] | = ptcb- > OSTCBBitX;
也就是OSTCBBitY保存的是组别,OSTCBBitX保存的是组内的偏移。而这两个变量是这么被初始化的:
ptcb- > OSTCBY = ( INT8U) ( prio > > 3) ;
ptcb- > OSTCBBitY = OSMapTbl[ ptcb- > OSTCBY] ;
ptcb- > OSTCBX = ( INT8U) ( prio & 0x07) ;
ptcb- > OSTCBBitX = OSMapTbl[ ptcb- > OSTCBX] ;
由于prio不会大于64,prio为6位值,因此OSTCBY为prio高3位,不会大于8,OSTCBX为prio低3位。
这里就涉及到OSMapTbl数组和OSUnMapTbl数组的用法了。我们先看看OSMapTbl和OSUnMapTbl的定义:
INT8U const OSMapTbl[ 8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80} ;
INT8U const OSUnMapTbl[ 256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
} ;
OSMapTbl分别是一个INT8U的八个位,而OSUnMap数组中的值就是从0x00到0xFF的八位中,每一个值所对应的最低位的值。我们在调度的时候只需将OSRdyGrp的值代入OSUnMapTbl数组中,得到OSUnMapTbl[ OSRdyGrp] 的值就是哪个优先级最高的Group有Ready进程存在,再使用该Group对应OSRdyTbl[ ] 数组中的值一样带入OSUnMapTbl中就可以得出哪个Task是优先级最高的。
于是我们提前来看看OS_Sched( ) 中获取最高优先级所使用的方法:
y = OSUnMapTbl[ OSRdyGrp] ; /* Get pointer to HPT ready to run */
OSPrioHighRdy = ( INT8U) ( ( y < < 3) + OSUnMapTbl[ OSRdyTbl[ y] ] ) ;
显然,先得到的y就是存在最高优先级的Group,然后OSUnMapTbl[ OSRdyTbl[ y] ] 就是Group中的偏移,因此OSPrioHighRdy最高优先级就应该是Group< < 3再加上这个偏移。
于是乎,我们就可以对上面那一小段很模糊的代码做一下总结:
prio只有6位,高3位代表着某一个Group保存在OSTCBY中,OSTCBBitY表示该Group所对应的Bit,将OSRdyGrp的该位置1表示该Group中有进程是Ready的;低3位代表着该Group中的第几个进程,保存在OSTCBX中,OSTCBBitX表示该进程在该Group中所对应的Bit,OSRdyTbl[ ptcb- > OSTCBY] | = ptcb- > OSTCBBitX就等于将该进程所对应的Bit置1了。
OSStart
OK,接下来我们来看这个开始函数了。OSStart其实很短,只有匆匆几句代码:
void OSStart ( void )
{
INT8U y;
INT8U x;
if ( OSRunning = = FALSE ) {
y = OSUnMapTbl[ OSRdyGrp] ; /* Find highest priority's task priority number */
x = OSUnMapTbl[ OSRdyTbl[ y] ] ;
OSPrioHighRdy = ( INT8U) ( ( y < < 3) + x) ;
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[ OSPrioHighRdy] ; /* Point to highest priority task ready to run */
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy( ) ; /* Execute target specific code to start task */
}
}
如果OSRunning为TRUE,表示OS已经在运行了,则OSStart不做任何事。
OSRunning为FALSE,则找出最高优先级的Ready的Task,并将该指针赋给OSTCBHighRdy和OSTCBCur。然后调用OSStartHighRdy( ) 开始运行该进程。
OSStartHighRdy( ) 为用户自定义函数,在这个函数中,主要功能就是进行堆栈切换并将OSRunning设置为TRUE表示OS已经开始运行,然后将保存的寄存器弹出,最后执行中断返回指令IRET就跳到OSTCBHighRdy的最开始处运行了。
OSTimeDly
在Task中,一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行,OSTimeDly将本进程从Ready TCBList中删除,然后将Delay的时间设置给OSTCBDly,最后调用OS_Sched进行进程调度。
void OSTimeDly ( INT16U ticks)
{
INT8U y;
if ( ticks > 0) { /* 0 means no delay! */
OS_ENTER_CRITICAL( ) ;
y = OSTCBCur- > OSTCBY; /* Delay current task */
OSRdyTbl[ y] & = ~ OSTCBCur- > OSTCBBitX;
if ( OSRdyTbl[ y] = = 0) {
OSRdyGrp & = ~ OSTCBCur- > OSTCBBitY;
}
OSTCBCur- > OSTCBDly = ticks; /* Load ticks in TCB */
OS_EXIT_CRITICAL( ) ;
OS_Sched( ) ; /* Find next task to run! */
}
}
如果ticks为零,说明不需延迟,则什么事情都不做。否则,调用OS_ENTER_CRITICAL进入临界段,将本进程从Ready TCBList中删除的代码如下:
y = OSTCBCur- > OSTCBY; /* Delay current task */
OSRdyTbl[ y] & = ~ OSTCBCur- > OSTCBBitX;
if ( OSRdyTbl[ y] = = 0) {
OSRdyGrp & = ~ OSTCBCur- > OSTCBBitY;
}
y为当前进程所在Group,OSRdyTbl[ y] 为该Group所在字节,& = ~ 则将该字节中本进程所占用的Bit清零。如果OSRdyTbl[ y] 为0,则说明这个Group中没有进程处于Ready状态,则将OSRdyGrp中该Group所占用的Bit清零。
然后将ticks保存在OSTCBDly中,每次OSTimeTick运行时会将这个值减一直至为零。
调用OS_EXIT_CRITICAL离开临界段,紧接着调用OS_Sched进入调度例程。
OS_Sched
OS_Sched是进程调度所使用的函数,在这里面找到最高优先级的进程,然后切换到该进程运行。
void OS_Sched ( void )
{
INT8U y;
OS_ENTER_CRITICAL( ) ;
if ( OSIntNesting = = 0) { /* Schedule only if all ISRs done and ... */
if ( OSLockNesting = = 0) { /* ... scheduler is not locked */
y = OSUnMapTbl[ OSRdyGrp] ; /* Get pointer to HPT ready to run */
OSPrioHighRdy = ( INT8U) ( ( y < < 3) + OSUnMapTbl[ OSRdyTbl[ y] ] ) ;
if ( OSPrioHighRdy ! = OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[ OSPrioHighRdy] ;
OSCtxSwCtr+ + ; /* Increment context switch counter */
OS_TASK_SW( ) ; /* Perform a context switch */
}
} %
阅读(828) | 评论(0) | 转发(0) |