Ucos源码分析------任务控制块与任务调度

Ucos源码分析

1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结



1. 任务控制块

typedef struct os_tcb {
    OS_STK        *OSTCBStkPtr;        //当前栈顶指针
    struct os_tcb *OSTCBNext;          //下一任务控制块指针
    struct os_tcb *OSTCBPrev;          //上一任务控制块指针

#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;          //任务消息控制块 与邮箱和队列有关
#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;           //任务优先级

    INT8U          OSTCBX;            //用于优先级的快速计算 
    INT8U          OSTCBY;             
    INT8U          OSTCBBitX;         
    INT8U          OSTCBBitY;          

#if OS_TASK_DEL_EN > 0
    BOOLEAN        OSTCBDelReq;       
#endif
} OS_TCB;

UCOS是通过任务控制块来管理任务的。任务控制块是一个基于链表的数据结构,基本成员如下

控制块成员
OSTCBStkPtr指向当前任务栈栈顶的指针。μC/OS-Ⅱ允许每个任务有自己的栈,每个任务的堆栈的容量可以是任意的。
OSTCBPre指向上一个任务控制块指针。用于任务控制块OS_TCB的链接。
OSTCBNext指向下一个任务控制块指针。用于任务控制块OS_TCB的链接。
OSTCBDly任务等待的延时时间变量。用于将任务挂起一段时间以等待某事件的发生,这种等待是有超时限制的。
OSTCBStat任务的当前状态标志变量。其为0时,任务进入就绪态 。
OSTCBPrio任务优先级变量。变量值越小,任务的优先级越高。

关于其中的OSTCBStat成员变量

宏名定义值说明
OS_STAT_RDY0x00运行准备就绪
OS_STAT_SEM0x01在信号量时挂起
OS_STAT_MBOX0x02在邮箱时挂起
OS_STAT_Q0x04在队列时挂起
OS_STAT_SUSPEND0x08任务被手动挂起暂停
OS_STAT_MUTEX0x10在互斥信号量时挂起
OS_STAT_FLAG0x20在事件标志组时挂起

2. 与任务控制有关的变量

4个指针、1个数组、1个指针数组

OSTCBCur指向“当前任务控制块”的指针
OSTCBHighRdy指向“将要运行最高优先级任务控制块”的指针
OSTCBFreeList“空任务控制块”链表的表头指针
OSTCBList“已使用控制块”链表的表头指针
OSTCBTbl[]任务控制块数组,所有的任务控制块都保存在这个数组中
OSTCBPrioTbl[]任务控制块优先级表,存放指向各任务控制块的指针。并按任务的优先级别将这些指针存放在数组的各个元素里

几个变量之间的关系
在这里插入图片描述

3.任务就绪表的放入与移除

相关变量:任务优先级Prio、任务就绪组OSRdyGrp、任务就绪表OSRdyTbl[ ]
**任务优先级表是8*8共64个,每一个8可以用任务优先级的3位来表示
在这里插入图片描述

需用用到一个映射表
映射表的作用,用以确定将哪一位置1

INT8U  const  OSMapTbl[]   = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

任务就绪表的放入

OSRdyGrp		|= OSMapTbl[Prio >> 3];
OSRdyTbl[Prio >> 3] 	|= OSMapTbl[Prio & 0x07];

任务就绪表的移除
移除时需要确定,移除任务之后,这个任务就绪组是否为0

If ((OSRdyTbl[Prio >> 3] &= ~OSMapTbl[Prio & 0x07]) == 0)
    OSRdyGrp &= ~OSMapTbl[Prio>>3];

举例:在这里插入图片描述

4.查找最高优先级的任务

采用的是是查表法,是通过解码映射表实现的。
功能是通过找到第一个出现1的位置编码的,下面是几个例子(0 :0X00 默认是0 )
1:0000 0001 第一个1的位置是0
8: 0000 1000 第一个1的位置是3
10: 0000 1010 第一个1的位置是1
所在任务的Y值越小优先级越高,所在任务的X值越小优先级越高,由此可见最小的Y、X值所对应的任务就是进入就绪态优先级最高的的任务。

INT8U  const  OSUnMapTbl[] = {
    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                             */
};

查找当前任务就绪表中最高优先级的任务

y	= OSUnMapTbl[OSRdyGrp];     //查找哪一行先被置1
x	= OSUnMapTbl[OSRdyTbl[y]];  //查找该行的哪一列先被置1
Prio	= (y << 3) + x;         //反推任务优先级

在任务控制块定义的 OSTCBX; OSTCBY; OSTCBBitX; OSTCBBitY;
可以快速获取任务优先级并改变;
就绪组和就绪表的修改依靠OSTCBBitY/X

OSTCBCur->OSTCBY//就绪表的索引
OSRdyGrp = OSTCBCur->OSTCBBitY                   //修改就绪组 OSRdyGrp
OSRdyTbl[OSTCBCur->OSTCBY] = OSTCBCur->OSTCBBitX//修改就绪表OSRdyTbl

关于最高任务优先级的查找:-----参考文章
有些处理器架构可以支持硬件查找,此时查找更快速

5.系统调度

OS_Sched ():系统调度函数

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr;
#endif    
    INT8U      y;

    //进入临界区
    OS_ENTER_CRITICAL();
    //OSIntNesting == 0判断是否在中断中进行任务调度
    if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & 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                     */
        }
    }
    OS_EXIT_CRITICAL();
}

void OSTimeTick (void):滴答中断
**滴答中断中只存在将任务放入到就绪表中,不存在任务调度
**当任务等待事件阻塞超时后,任务状态为等待事件状态,任务却在任务就绪表中

void  OSTimeTick (void)
{
#if OS_CRITICAL_METHOD == 3                                /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr;
#endif    
    OS_TCB    *ptcb;

    //提供给外部的钩子函数
    OSTimeTickHook();                                      /* Call user definable hook                 */
#if OS_TIME_GET_SET_EN > 0   
    OS_ENTER_CRITICAL();                                   /* Update the 32-bit tick counter           */
    //系统时间更新
    OSTime++;
    OS_EXIT_CRITICAL();
#endif
    //系统处于运行状态
    if (OSRunning == TRUE) {    
        //获取已经使用的任务控制块链表的表头
        ptcb = OSTCBList;                                  /* Point at first TCB in TCB list           */
        //遍历所有的任务
        //直到空闲任务,
        while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {          /* Go through all TCBs in TCB list          */
            OS_ENTER_CRITICAL();
            //任务延时-1
            if (ptcb->OSTCBDly != 0) {                     /* Delayed or waiting for event with TO     */
                //如果任务超时了
                if (--ptcb->OSTCBDly == 0) {               /* Decrement nbr of ticks to end of delay   */
                     //判断任务是否是主动挂起
                    if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended?    */  
                        //任务不是被主动挂起的,需要将其加入到就绪表
                        OSRdyGrp               |= ptcb->OSTCBBitY; /* No,  Make task R-to-R (timed out)*/
                        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    } else {                               /* Yes, Leave 1 tick to prevent ...         */
                        //判断任务是主动挂起的重新设置延时为1
                        ptcb->OSTCBDly = 1;                /* ... loosing the task when the ...        */
                    }                                      /* ... suspension is removed.               */
                }
            }
            //下一个任务的任务控制块
            ptcb = ptcb->OSTCBNext;                        /* Point at next TCB in TCB list            */
            OS_EXIT_CRITICAL();
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值