uC/OS-II源码分析(四)

内核结构

1,  临界区,OS_ENTER_CRITICALOS_EXIT_CRITICAL

为了处理临界区代码,必须关中断,等处理完毕后,再开中断。关中断可以避免其他任务或中断进入临界区代码。uC/OS-II定义了这两个宏来实现,但注意一条:调用uC/OS-II功能函数时,中断应该总是开着的。

      1)当OS_CRITICAL_METHOD= = 1时,简单实现如下:

     #define  OS_ENTER_CRITICAL() disable_int() 
    # define OS_EXIT_CRITICAL() enable_int()

 

但这样有一个问题,如果禁止中断的情况下调用uC/OS-II功能函数,那么从功能函数返回时,中断可能变成允许的了,而实际上还是希望是禁止的。

      2)当OS_CRITICAL_METHOD= = 2时,实现如下:

     #define  OS_ENTER_CRITICAL()    
            asm(“PUSH PSW”);
            asm(“DI”);
    
#define  OS_EXIT_CRITICAL()
            asm(“POP PSW”);

执行OS_ENTER_CRITICAL()时,先将中断状态保存到堆栈,然后关中断;执行OS_EXIT_CRITICAL()时,再从堆栈中恢复原来的中断开/关状态。这种方法不会改变中断状态,避免前面的问题。

      3)当OS_CRITICAL_METHOD= = 3时,实现如下:

     #define  OS_ENTER_CRITICAL()    
        cpu_sr 
=  get_processor_psw();
        disable_interrupts();
    
#define  OS_EXIT_CRITICAL()
        set_ processor_psw(cpu_sr);

      将处理器状态字保存在局部变量中。

2,  任务是一个无限循环,返回类型为void,参数void*,用于传数据给任务。任务可以调用OSTaskDel(OS_PRIO_SELF)进行自我删除。任务有5种状态:

1)     睡眠态。任务驻留在程序空间(ROMRAM,还未交给uC/OS-II来管理。

2)     就绪态。OSTaskCreate()OSTaskCreateExt()来创建一个任务后,就进入就绪态。任务可以调用OSTaskDel返回到睡眠态,或调用该函数让另一个任务进入睡眠态。

3)     运行态。OSStart()启动多任务运行。它只在启动时调用一次,运行就绪列表中优先级最高的任务。就绪的任务只有当所以优先级比其高的任务转为等待状态,或者是被删除了,才能进入运行态。

4)     等待状态。正在运行的任务可以调用OSTimeDly()OSTimeDlyHMSM()将自身延迟一段时间进入等待状态,一直到延迟时间到来。这两个函数会强制执行任务切换,选择下一个优先级最高的任务运行。等待时间过去后,系统服务函数OSTimeTick()使延迟了的任务进入就绪态。

正在运行的任务也可能需要等待某一事件的发生,可以调用:OSFlagPend(),OSSemPend(),OSMutexPend(),OSMboxPend(),OSQPend()等函数。若某事件未发生,则任务进入等待状态,直到事件发生。当任务因等待事件被挂起时,下一个优先级最高的任务得到CPU。当事件发生了或超时,被挂起的任务进入就绪态。事件发生的报告可能来自另一个任务或中断服务子程序。

5)     中断服务态 。被中断的任务进入中断服务态,从而被挂起,中断服务子程序得到CPU,后者可能报告一个或多个事件发生,从而使一个或多个任务进入就绪态。因此从中断服务子程序返回前,uC/OS-II要判断被中断的任务的优先级和就绪列表中其他任务的优先级高低,选择最高的任务进入运行态。

6)     当所以任务都在等待事件发生或等待延迟的时间结束时,uC/OS-II运行OSTaskIdle()任务。

3,  任务控制块(OS_TCB)

建立一个任务时,一个OS_TCB就被赋值。当任务的CPU被剥夺时,用它来保存任务的状态,当任务重新得到CPU时,它也能保证任务从当时被中断的那一点继续执行。OS_TCB全部驻留在RAM中。

typedef  struct  os_tcb 
{
    OS_STK        
* OSTCBStkPtr;         /* 指向当前任务堆栈栈顶的指针 */
#if  OS_TASK_CREATE_EXT_EN > 0
    
void            * OSTCBExtPtr;         /*  指向用户定义的任务控制块扩展 */
    OS_STK        
* OSTCBStkBottom;      /*  指向栈底的指针 */
    INT32U         OSTCBStkSize;       
/*  栈中可容纳的元素数目(
uC/OS-II允许每个任务的堆栈容量任意,)
*/
    INT16U         OSTCBOpt;           
/*  传给OSTaskCreateExt()的任务选择项 */
    INT16U         OSTCBId;            
/*  Task ID (0..65535)  */
#endif
    
struct  os_tcb  * OSTCBNext;           /* TCB列表中指向下一个TCB的指针 */
    
struct  os_tcb  * OSTCBPrev;           /*  TCB列表中指向上一个TCB的指针 */
#if  ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
    OS_EVENT      
* OSTCBEventPtr;       /* 指向事件控制块的指针 */
#endif
#if  ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
    
void            * OSTCBMsg;            /* 指向传递给任务的消息的指针,消息来自OSMboxPost() or OSQPost()  */
#endif
#if  (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if  OS_TASK_DEL_EN > 0
    OS_FLAG_NODE  
* OSTCBFlagNode;       /* 指向事件标志节点的指针 */
#endif     
    OS_FLAGS       OSTCBFlagsRdy;      
/*  使任务进入就绪态的事件标志 */
#endif
    INT16U         OSTCBDly;           
/*  让任务延时若干节拍或把任务挂起一段时间等待某一事件发生时使用的计时变量 */
    INT8U          OSTCBStat;          
/*  任务状态 */
    INT8U          OSTCBPrio;          
/*  任务优先级(0 == highest, 63 == lowest) */
    
// 下面四个变量用于加速任务进入就绪态或进入等待事件发生状态的过程,算法比较巧妙
    INT8U          OSTCBX;              /*  Bit position in group  corresponding to task priority (0..7)  */
    INT8U          OSTCBY;             
/*  Index into ready table corresponding to task priority        
    INT8U          OSTCBBitX;          /* Bit mask to access bit position in ready table               
    INT8U          OSTCBBitY;          /* Bit mask to access bit position in ready group               
#if OS_TASK_DEL_EN > 0
    BOOLEAN        OSTCBDelReq;        /* 表面任务是否需要删除自己
*/
#endif
} OS_TCB;

 

uC/OS-II在通过OS_MAX_TASKS定义了最大的任务数目,这也决定了分配给用户程序的任务控制块OS_TCB的数目,但此外uC/OS-II还分配给系统任务OS_N_SYS_TASKS若干个额外的任务控制块,供其内部使用。uC/OS-II初始化时,所以任务控制块都被链接成单向空任务链表,任务一旦建立,空任务控制块指针OSTCBFreeList指向的任务控制块就赋给该任务,然后OSTCBFreeList调整为指向链表中下一个空任务控制块。一旦任务被删除,任务控制块被还给空任务链表。

      任务建立时,OS_TCBInit()初始化任务控制块,函数OSTaskCreate()OSTaskCreateExt()调用任务控制块初始化函数TCBInit

INT8U  OS_TCBInit (INT8U prio, OS_STK  * ptos, OS_STK  * pbos, INT16U id, INT32U stk_size,  void   * pext, INT16U opt)
{
#if  OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配内存*/
    OS_CPU_SR  cpu_sr;
#endif     
    OS_TCB    
* ptcb;
    OS_ENTER_CRITICAL();
    ptcb 
=  OSTCBFreeList;                                   /*  从空闲TCB链表中取一个空TCB */
    
if  (ptcb  !=  (OS_TCB  * ) 0
    {
// 取到了空TCB
        OSTCBFreeList         =  ptcb -> OSTCBNext;             /*  更新空闲TCB链表头指针 */
        OS_EXIT_CRITICAL();
        ptcb
-> OSTCBStkPtr     =  ptos;                        /*  保存栈顶指针到TCB */
        ptcb
-> OSTCBPrio       =  (INT8U)prio;                 /*  保存任务优先级到TCB */
        ptcb
-> OSTCBStat       =  OS_STAT_RDY;                 /*  任务状态设置为就绪 */
        ptcb
-> OSTCBDly        =   0 ;                           /*  任务不延时等待 */
#if  OS_TASK_CREATE_EXT_EN > 0 // 使用扩展
        ptcb
-> OSTCBExtPtr     =  pext;                        /*  保存TCB扩展指针 */
        ptcb
-> OSTCBStkSize    =  stk_size;                    /*  保存栈大小 */
        ptcb
-> OSTCBStkBottom  =  pbos;                        /*  保存栈底 */
        ptcb
-> OSTCBOpt        =  opt;                         /*  保存任务选择项 */
        ptcb
-> OSTCBId         =  id;                          /* 保存任务ID */
#else // 不使用扩展
        pext                 
=  pext;                       
        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          =  prio  >>   3 ;                   /*  预先计算X, Y, BitX and BitY */
        ptcb
-> OSTCBBitY       =  OSMapTbl[ptcb -> OSTCBY];
        ptcb
-> OSTCBX          =  prio  &   0x07 ;
        ptcb
-> OSTCBBitX       =  OSMapTbl[ptcb -> OSTCBX];

#if  OS_EVENT_EN > 0
        ptcb
-> OSTCBEventPtr   =  (OS_EVENT  * ) 0 ;               /*  任务不等待任何事件发生 */
#endif
#if  (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) && (OS_TASK_DEL_EN > 0)
        ptcb
-> OSTCBFlagNode   =  (OS_FLAG_NODE  * ) 0 ;           /*  任务不等待事件标志 */
#endif
#if  (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
        ptcb
-> OSTCBMsg        =  ( void   * ) 0 ;                   /*  没有接收任何消息 */
#endif
#if  OS_VERSION >= 204
        OSTCBInitHook(ptcb);
#endif
        OSTaskCreateHook(ptcb);                            
/*  调用用户定义的钩子函数 */
        OS_ENTER_CRITICAL();
        OSTCBPrioTbl[prio] 
=  ptcb;
        ptcb
-> OSTCBNext     =  OSTCBList;                     /* 将TCB插入已经建立任务的双向链表中 */
        ptcb
-> OSTCBPrev     =  (OS_TCB  * ) 0 ;
        
if  (OSTCBList  !=  (OS_TCB  * ) 0 ) {
            OSTCBList
-> OSTCBPrev  =  ptcb;
        }
        OSTCBList               
=  ptcb;
        OSRdyGrp               
|=  ptcb -> OSTCBBitY;          /* 让用户进入就绪态 */
        OSRdyTbl[ptcb
-> OSTCBY]  |=  ptcb -> OSTCBBitX;
        OS_EXIT_CRITICAL();
        
return  (OS_NO_ERR);
    }
    OS_EXIT_CRITICAL();
    
return  (OS_NO_MORE_TCB);
}

 

你可能感兴趣的:(源码分析)