uC/OS-II源码分析

来源:本站
导读:目前正在解读《uC/OS-II源码分析》的相关信息,《uC/OS-II源码分析》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《uC/OS-II源码分析》的详细说明。

uC/OS-II源码分析(总体思路一)

首先从main函数开始,下面是uC/OS-IImain函数的大致流程:

main(){

OSInit();

TaskCreate(...);

OSStart();}

首先是调用OSInit进行初始化,然后使用TaskCreate创建几个进程/Task,最后调用OSStart,操作系统就开始运行了。

OSInit

最先看看OSInit完成哪些初始化:

voidOSInit(void)

{

#ifOS_VERSION>=204

OSInitHookBegin();

#endif

OS_InitMisc();

OS_InitRdyList();

OS_InitTCBList();

OS_InitEventList();

#if(OS_VERSION>=251)&&(OS_FLAG_EN>0)&&(OS_MAX_FLAGS>0)

OS_FlagInit();

#endif

#if(OS_MEM_EN>0)&&(OS_MAX_MEM_PART>0)

OS_MemInit();

#endif

#if(OS_Q_EN>0)&&(OS_MAX_QS>0)

OS_QInit();

#endif

OS_InitTaskIdle();

#ifOS_TASK_STAT_EN>0

OS_InitTaskStat();

#endif

#ifOS_VERSION>=204

OSInitHookEnd();

#endif

#ifOS_VERSION>=270&&OS_DEBUG_EN>0

OSDebugInit();

#endif

}

OS_InitMisc()完成的是一些其其他他的变量的初始化:

OSIntNesting=0;

OSLockNesting=0;

OSTaskCtr=0;

OSRunning=FALSE;

OSCtxSwCtr=0;

OSIdleCtr=0L;

其中包括:中断嵌套标志OSIntNesting,调度锁定标志OSLockNesting,OS标志OSRunning等。OSRunning在这里设置为FALSE,在后面OSStartHighRdy中会被设置为TRUE表示OS开始工作。

OS_InitRdyList()初始化就绪Task列表:

staticvoidOS_InitRdyList(void)

{

INT8Ui;

INT8U*prdytbl;

OSRdyGrp=0x00;

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列表。

staticvoidOS_InitTCBList(void)

{

INT8Ui;

OS_TCB*ptcb1;

OS_TCB*ptcb2;

OS_MemClr((INT8U*)&OSTCBTbl[0],sizeof(OSTCBTbl));

OS_MemClr((INT8U*)&OSTCBPrioTbl[0],sizeof(OSTCBPrioTbl));

ptcb1=&OSTCBTbl[0];

ptcb2=&OSTCBTbl[1];

for(i=0;i<(OS_MAX_TASKS+OS_N_SYS_TASKS-1);i++){

ptcb1->OSTCBNext=ptcb2;

#ifOS_TASK_NAME_SIZE>1

ptcb1->OSTCBTaskName[0]='?';

ptcb1->OSTCBTaskName[1]=OS_ASCII_NUL;

#endif

ptcb1++;

ptcb2++;

}

ptcb1->OSTCBNext=(OS_TCB*)0;

#ifOS_TASK_NAME_SIZE>1

ptcb1->OSTCBTaskName[0]='?';

ptcb1->OSTCBTaskName[1]=OS_ASCII_NUL;

#endif

OSTCBList=(OS_TCB*)0;

OSTCBFreeList=&OSTCBTbl[0];

}

这里完成的工作很简单,首先把整个数组使用OSTCBNext指针连接成链表链起来,然后将OSTCBList初始化为0,也就是还没有TCB,因为还没有Task产生,OSTCBFreeList指向OSTCBTbl[]数组的第一个表示所有TCB都处于Free状态。

OS_InitEventList()初始化Event列表。

staticvoidOS_InitEventList(void)

{

#ifOS_EVENT_EN&&(OS_MAX_EVENTS>0)

#if(OS_MAX_EVENTS>1)

INT16Ui;

OS_EVENT*pevent1;

OS_EVENT*pevent2;

OS_MemClr((INT8U*)&OSEventTbl[0],sizeof(OSEventTbl));

pevent1=&OSEventTbl[0];

pevent2=&OSEventTbl[1];

for(i=0;i<(OS_MAX_EVENTS-1);i++){

pevent1->OSEventType=OS_EVENT_TYPE_UNUSED;

pevent1->OSEventPtr=pevent2;

#ifOS_EVENT_NAME_SIZE>1

pevent1->OSEventName[0]='?';

pevent1->OSEventName[1]=OS_ASCII_NUL;

#endif

pevent1++;

pevent2++;

}

pevent1->OSEventType=OS_EVENT_TYPE_UNUSED;

pevent1->OSEventPtr=(OS_EVENT*)0;

#ifOS_EVENT_NAME_SIZE>1

pevent1->OSEventName[0]='?';

pevent1->OSEventName[1]=OS_ASCII_NUL;

#endif

OSEventFreeList=&OSEventTbl[0];

#else

OSEventFreeList=&OSEventTbl[0];

OSEventFreeList->OSEventType=OS_EVENT_TYPE_UNUSED;

OSEventFreeList->OSEventPtr=(OS_EVENT*)0;

#ifOS_EVENT_NAME_SIZE>1

OSEventFreeList->OSEventName[0]='?';

OSEventFreeList->OSEventName[1]=OS_ASCII_NUL;

#endif

#endif

#endif

}

同样将EventTbl[]数组中的OSEventType都初始化为OS_EVENT_TYPE_UNUSED。

OS_InitTaskIdle(),中间我们跳过其他的如Mem等的初始化,看看IdleTask的初始化。

(void)OSTaskCreateExt(OS_TaskIdle,

(void*)0,

&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE-1],

OS_IDLE_PRIO,

OS_TASK_IDLE_ID,

&OSTaskIdleStk[0],

OS_TASK_IDLE_STK_SIZE,

(void*)0,

OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);

其实IdleTask的初始化很简单就是调用OSTaskCrete系列的函数创建一个Task,OSTaskCreate我们后面再做进一步分析。

初始化StateTask也是类似调用OSTaskCreate系列函数创建StatTask。这里只是创建了该Task的各个结构还没有真正运行该Task,直到OSStart中才依据优先级调度运行。

OK,到这里OSInit算高一个段落了,我们接着回到main往下看

uC/OS-II源码分析(总体思路二)

OSTaskCreate负责创建Task所需的数据结构,该函数原形如下所示:

INT8UOSTaskCreate(void(*task)(void*pd),void*p_arg,OS_STK*ptos,INT8Uprio)

其中task是一个函数指针,指向该Task所开始的函数,当这个Task第一次被调度运行时将会从task处开始运行。

p_arg是传给task的参数指针;

ptos是堆栈指针,指向栈顶(堆栈从上往下)或栈底(堆栈从下往上);

prio是进程的优先级,uC/OS-II共支持最大64个优先级,其中最低的两个优先级给Idle和Stat进程,并且各个Task的优先级必须不同。

接下来,我们看看这个函数的执行流程:

#ifOS_ARG_CHK_EN>0

if(prio>OS_LOWEST_PRIO){

return(OS_PRIO_INVALID);

}

#endif

OS_ENTER_CRITICAL();

if(OSIntNesting>0){

OS_EXIT_CRITICAL();

return(OS_ERR_TASK_CREATE_ISR);

}

if(OSTCBPrioTbl[prio]==(OS_TCB*)0){

OSTCBPrioTbl[prio]=(OS_TCB*)1;

OS_EXIT_CRITICAL();

psp=(OS_STK*)OSTaskStkInit(task,p_arg,ptos,0);

err=OS_TCBInit(prio,psp,(OS_STK*)0,0,0,(void*)0,0);

if(err==OS_NO_ERR){

if(OSRunning==TRUE){

OS_Sched();

}

}else{

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio]=(OS_TCB*)0;

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,INT16Uopt)

和OSTaskCreate类似,task是进程入口地址,pdata是参数地址,ptos是堆栈指针,而opt只是作为一个预留的参数Option而保留。返回的是调整以后的堆栈指针。

在OSTaskStkInit中,一般是将pdata入栈,flag入栈,task入栈,然后将各寄存器依次入栈。

OS_TCBInit初始化TCB数据结构,下面只提取主要部分来看:

INT8UOS_TCBInit(INT8Uprio,OS_STK*ptos,OS_STK*pbos,INT16Uid,INT32Ustk_size,void*pext,INT16Uopt)

{

OS_TCB*ptcb;

OS_ENTER_CRITICAL();

ptcb=OSTCBFreeList;

if(ptcb!=(OS_TCB*)0){

OSTCBFreeList=ptcb->OSTCBNext;

OS_EXIT_CRITICAL();

ptcb->OSTCBStkPtr=ptos;

ptcb->OSTCBPrio=prio;

ptcb->OSTCBStat=OS_STAT_RDY;

ptcb->OSTCBPendTO=FALSE;

ptcb->OSTCBDly=0;

#ifOS_TASK_CREATE_EXT_EN>0

ptcb->OSTCBExtPtr=pext;

ptcb->OSTCBStkSize=stk_size;

ptcb->OSTCBStkBottom=pbos;

ptcb->OSTCBOpt=opt;

ptcb->OSTCBId=id;

#else

pext=pext;

stk_size=stk_size;

pbos=pbos;

opt=opt;

id=id;

#endif

#ifOS_TASK_DEL_EN>0

ptcb->OSTCBDelReq=OS_NO_ERR;

#endif

ptcb->OSTCBY=(INT8U)(prio>>3);

ptcb->OSTCBBitY=OSMapTbl[ptcb->OSTCBY];

ptcb->OSTCBX=(INT8U)(prio&0x07);

ptcb->OSTCBBitX=OSMapTbl[ptcb->OSTCBX];

#ifOS_EVENT_EN

ptcb->OSTCBEventPtr=(OS_EVENT*)0;

#endif

OSTaskCreateHook(ptcb);

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio]=ptcb;

ptcb->OSTCBNext=OSTCBList;

ptcb->OSTCBPrev=(OS_TCB*)0;

if(OSTCBList!=(OS_TCB*)0){

OSTCBList->OSTCBPrev=ptcb;

}

OSTCBList=ptcb;

OSRdyGrp|=ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

OSTaskCtr++;

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的定义:

INT8UconstOSMapTbl[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};

INT8UconstOSUnMapTbl[256]={

从略………………

};

OSMapTbl分别是一个INT8U的八个位,而OSUnMap数组中的值就是从0x00到0xFF的八位中,每一个值所对应的最低位的值。我们在调度的时候只需将OSRdyGrp的值代入OSUnMapTbl数组中,得到OSUnMapTbl[OSRdyGrp]的值就是哪个优先级最高的Group有Ready进程存在,再使用该Group对应OSRdyTbl[]数组中的值一样带入OSUnMapTbl中就可以得出哪个Task是优先级最高的。

于是我们提前来看看OS_Sched()中获取最高优先级所使用的方法:

y=OSUnMapTbl[OSRdyGrp];

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其实很短,只有匆匆几句代码:

voidOSStart(void)

{

INT8Uy;

INT8Ux;

if(OSRunning==FALSE){

y=OSUnMapTbl[OSRdyGrp];

x=OSUnMapTbl[OSRdyTbl[y]];

OSPrioHighRdy=(INT8U)((y<<3)+x);

OSPrioCur=OSPrioHighRdy;

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];

OSTCBCur=OSTCBHighRdy;

OSStartHighRdy();

}

}

如果OSRunning为TRUE,表示OS已经在运行了,则OSStart不做任何事。

OSRunning为FALSE,则找出最高优先级的Ready的Task,并将该指针赋给OSTCBHighRdy和OSTCBCur。然后调用OSStartHighRdy()开始运行该进程。

OSStartHighRdy()为用户自定义函数,在这个函数中,主要功能就是进行堆栈切换并将OSRunning设置为TRUE表示OS已经开始运行,然后将保存的寄存器弹出,最后执行中断返回指令IRET就跳到OSTCBHighRdy的最开始处运行了。

uC/OS-II源码分析(总体思路三)

OSTimeDly

在Task中,一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行,OSTimeDly将本进程从ReadyTCBList中删除,然后将Delay的时间设置给OSTCBDly,最后调用OS_Sched进行进程调度。

voidOSTimeDly(INT16Uticks)

{

INT8Uy;

if(ticks>0){

OS_ENTER_CRITICAL();

y=OSTCBCur->OSTCBY;

OSRdyTbl[y]&=~OSTCBCur->OSTCBBitX;

if(OSRdyTbl[y]==0){

OSRdyGrp&=~OSTCBCur->OSTCBBitY;

}

OSTCBCur->OSTCBDly=ticks;

OS_EXIT_CRITICAL();

OS_Sched();

}

}

如果ticks为零,说明不需延迟,则什么事情都不做。否则,调用OS_ENTER_CRITICAL进入临界段,将本进程从ReadyTCBList中删除的代码如下:

y=OSTCBCur->OSTCBY;

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是进程调度所使用的函数,在这里面找到最高优先级的进程,然后切换到该进程运行。

voidOS_Sched(void)

{

INT8Uy;

OS_ENTER_CRITICAL();

if(OSIntNesting==0){

if(OSLockNesting==0){

y=OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy=(INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);

if(OSPrioHighRdy!=OSPrioCur){

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;

OS_TASK_SW();

}

}

}

OS_EXIT_CRITICAL();

}

OS_Sched不允许在中断嵌套中调用,因此先判断是否是中断嵌套,并且是否限制进程调度,这两个条件都满足之后,找到最高优先级的进程,如果这个进程不是当前进程,则将新的进程TCB指针保存到OSTCBHighRdy中,为调度计数器OSCtxSwCtr加一,然后调用宏OS_TASK_SW()进行切换。

OS_TASK_SW()宏也是一个自定义的宏,uC/OS-II推荐使用软中断方式实现。

OSCtxSw是一个中断响应函数,一般我们在初始化时将这个软终端和OSCtxSw挂接好。在OSCtxSw中所需要做的事情就是将当前寄存器的值保存到当前堆栈中,然后切换堆栈到新进程的堆栈,将寄存器的值出栈,然后调用中断返回指令IRET就返回到新进程中断前的地方继续执行了。

定时中断

uC/OS-II的定时中断必须在OSStart之后初始化,而不能在OSStart之前,因为害怕第一个TimeTick发生时第一个进程还没有开始运行,而这时uC/OS是处于不可预期状态,会导致死机。

因此对于定时中断,我一般是放在最高级进程的初始化中进行,然后将定时中断和OSTickISR挂接。

OSTickISR也是一个用户自定义函数,所要完成的功能一个是保存当前的寄存器到当前堆栈将OSIntNesting加一,然后调用uC/OS提供的OSTimeTick函数,然后调用OSIntExit()将OSIntNesting减一,最后将各寄存器值出栈,使用中断返回指令IRET返回。

OSTimeTick在每个时钟中断中被调用一次,在该函数中会更新各个进程TCB所对应的OSTCBDly,如果该OSTCBDly减为0,则对应的TCB就被放入ReadyTCBList中。

OS_ENTER_CRITICAL();

OSTime++;

OS_EXIT_CRITICAL();

ptcb=OSTCBList;

while(ptcb->OSTCBPrio!=OS_IDLE_PRIO){

OS_ENTER_CRITICAL();

if(ptcb->OSTCBDly!=0){

if(--ptcb->OSTCBDly==0){

if((ptcb->OSTCBStat&OS_STAT_PEND_ANY)!=OS_STAT_RDY){

ptcb->OSTCBStat&=~OS_STAT_PEND_ANY;

ptcb->OSTCBPendTO=TRUE;

}else{

ptcb->OSTCBPendTO=FALSE;

}

if((ptcb->OSTCBStat&OS_STAT_SUSPEND)==OS_STAT_RDY){

OSRdyGrp|=ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

}

}

}

ptcb=ptcb->OSTCBNext;

OS_EXIT_CRITICAL();

}

首先在临界段将OSTime加一,然后遍历整个非Free的TCBList,如果OSTCBDly不为0,则,将OSTCBDly减一,如果这时OSTCBDly为0,而且TCB对应的进程需要等待任何信号量或Event等,则说明超时时间到了,将当前TCB的State中OS_STAT_PEND_ANY位去掉,然后将OSTCBPendTo设置为TRUE,表示这是PEND的超时,否则设置OSTCBPendTO为FALSE。

如果OSTCBDly减为零,且该进程没有Suspend,则将该进程放入ReadyTCBList中,使用方法同TaskCreate中的方法。

然后我们来说说OSIntExit这个函数。该函数代码如下:

voidOSIntExit(void)

{

INT8Uy;

if(OSRunning==TRUE){

OS_ENTER_CRITICAL();

if(OSIntNesting>0){

OSIntNesting--;

}

if(OSIntNesting==0){

if(OSLockNesting==0){

y=OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy=(INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);

if(OSPrioHighRdy!=OSPrioCur){

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;

OSIntCtxSw();

}

}

}

OS_EXIT_CRITICAL();

}

}

首先判断OSRunning是否为1,也就是OS是否在运行,当然没有运行就什么都不做。

然后将OSIntNesting减一,这个是需要在临界段进行的。如果OSIntNesting减为零,并且没有限制进程切换,则找到当前最高优先级的进程(方法同OS_Sched()),然后调用OSIntCtxSw进行进程切换。

OSIntCtxSw()是用户自定义函数,该函数的主要功能与OSCtxSw类似,只是需要对当前的堆栈进行稍微的调整,将OSIntExit和OSIntCtxSw调用所需要的堆栈去掉,然后做的和OSCtxSw一样。

在实际的Porting中发现要去掉OSIntExit和OSIntCtxSw调用所占用的堆栈还是比较麻烦的,因此我就现在OSTickISR刚开始的时候保存好现场之后就将堆栈指针赋给当前进程TCB的OSStkPtr,这样,在OSIntCtxSw中就不需要重新对当前堆栈的值进行保存,只需进行切换就可以了

堆栈结构

提醒:《uC/OS-II源码分析》最后刷新时间 2024-03-14 01:07:22,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《uC/OS-II源码分析》该内容的真实性请自行鉴别。