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

本文详细分析了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();
        }
    }
}
\SOFTWARE The main directory from the root where all software-related files are placed. \SOFTWARE\BLOCKS The main directory where all ‘Building Blocks’ are located. With μC/OS-II, I included a ‘building block’ that handles DOS-type compatible functions that are used by the example code. \SOFTWARE\BLOCKS\TO This directory contains the files for the TO utility (see Appendix E, TO). The source file is TO.C and is found in the \SOFTWARE\TO\SOURCE directory. The DOS executable file (TO.EXE) is found in the \SOFTWARE\TO\EXE directory. Note that TO requires a file called TO.TBL which must reside on your root directory. An example of TO.TBL is also found in the \SOFTWARE\TO\EXE directory. You will need to move TO.TBL to the root directory if you are to use TO.EXE. \SOFTWARE\uCOS-II The main directory where all μC/OS-II files are located. \SOFTWARE\uCOS-II\EX1_x86L This directory contains the source code for EXAMPLE #1 (see section 1.07, Example #1) which is intended to run under DOS (or a DOS window under Windows 95). \SOFTWARE\uCOS-II\EX2_x86L This directory contains the source code for EXAMPLE #2 (see section 1.08, Example #2) which is intended to run under DOS (or a DOS window under Windows 95). \SOFTWARE\uCOS-II\EX3_x86L This directory contains the source code for EXAMPLE #3 (see section 1.09, Example #3) which is intended to run under DOS (or a DOS window under Windows 95). \SOFTWARE\uCOS-II\Ix86L This directory contains the source code for the processor dependent code (a.k.a. the port) of μC/OS-II for an 80x86 Real-Mode, Large Model processor. \SOFTWARE\uCOS-II\SOURCE This directory contains the source code for processor independent portion of μC/OS-II. This code is fully portable to other processor architectures.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值