Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181098
  • 博文数量: 50
  • 博客积分: 1053
  • 博客等级: 少尉
  • 技术积分: 577
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-05 22:55
文章分类

全部博文(50)

文章存档

2016年(2)

2015年(1)

2014年(6)

2012年(11)

2011年(22)

2010年(5)

2009年(3)

我的朋友

分类:

2010-07-29 16:24:44

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-%

阅读(758) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~