UCOS-II学习笔记之任务管理

在UCOS-II嵌入式操作系统中,对任务的管理,主要掌握的点有任务结构体、单/双链表、优先级就绪表、任务相关操作(创建、删除等)等。

1. 任务结构体介绍

任务结构体的成员为人物相关的信息,方便管理任务。

typedef struct os_tcb    声明一个结构体类型,typedef的作用是给struct os_tcb起别名OS_TCB,定义结构体变量时,直接使用别名即可。可以省去struct关键字。比如,定义结构体变量a时,可以OS_TCB a; 而不需要 struct os_tcb  a;
{
    OS_STK          *OSTCBStkPtr;           /* 指向任务私有栈的栈顶,每个任务都有一个独自的私有栈,在任务切换时保存现场   */

#if OS_TASK_CREATE_EXT_EN > 0u              条件编译,根据需要可在配置文件中使能自己需要的功能,系统可裁剪体现在这。这里是人物扩展功能
    void            *OSTCBExtPtr;                                            /* 任务扩展指针      */
    OS_STK          *OSTCBStkBottom;                                         /* 指向任务栈底                        */
    INT32U           OSTCBStkSize;                                           /* 栈的大小      */
    INT16U           OSTCBOpt;                                               /*可选项        */
    INT16U           OSTCBId;                                                /* 任务I D  */
#endif


    struct os_tcb   *OSTCBNext;             /* 双向链表,指向链表的下一个            */
    struct os_tcb   *OSTCBPrev;             /* 双向链表,指向链表的上一个           */


#if (OS_EVENT_EN)下面这部分主要用于任务间通讯,信号量、消息邮箱、阵列等
    OS_EVENT        *OSTCBEventPtr;         /* 指向事件控制块指针              */
#endif
    #if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)
    OS_EVENT       **OSTCBEventMultiPtr;    /* 指向多事件控制块的指针             */
#endif
    #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)下面这部分主要用于任务间通讯,信号量、消息邮箱、阵列等
    void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
#endif
    #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
#if OS_TASK_DEL_EN > 0u
    OS_FLAG_NODE    *OSTCBFlagNode;         /* Pointer to event flag node                              */
#endif
    OS_FLAGS         OSTCBFlagsRdy;         /* Event flags that made task ready to run                 */
#endif




    INT32U           OSTCBDly;              /* 延时参数,比如任务等待一个事件所要的时间  */
    INT8U            OSTCBStat;             /* 指示任务当前的状态                             */
    INT8U            OSTCBStatPend;         /* 任务挂起的状态                                  */
    INT8U            OSTCBPrio;             /*任务优先级,每个任务都有一个优先级,并且每个优先级仅有一个任务,0优先级最高                       */

    INT8U            OSTCBX;                /* 当十进制时,优先级0-63,前两位无效,只看后6为,该值表示低三位的值   */
    INT8U            OSTCBY;                /* 优先级后六位中的高三位的值  */
    OS_PRIO          OSTCBBitX;             /* 该值对应优先级就绪表中的第几列,    */
    OS_PRIO          OSTCBBitY;             /* 该值对应优先就绪表中的第几行,即优先级就绪组        */

#if OS_TASK_DEL_EN > 0u
    INT8U            OSTCBDelReq;           /* 标识位,设置后告知任务删除         */
#endif

#if OS_TASK_PROFILE_EN > 0u
    INT32U           OSTCBCtxSwCtr;         /* 计数任务被切换的次数              */
    INT32U           OSTCBCyclesTot;        /*该任务运行时钟周期的总数量 */
    INT32U           OSTCBCyclesStart;      /* Snapshot of cycle counter at start of task resumption   */
    OS_STK          *OSTCBStkBase;          /* Pointer to the beginning of the task stack              */
    INT32U           OSTCBStkUsed;          /* Number of bytes used from the stack                     */
#endif

#if OS_TASK_NAME_EN > 0u
    INT8U           *OSTCBTaskName;
#endif

#if OS_TASK_REG_TBL_SIZE > 0u
    INT32U           OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
#endif
} OS_TCB;

2 任务初始化

任务初始化的目的,把空任务控制块组成一个单向链表,当需创建一个任务时,就从链表中取下一个空任务块;当释放一个任务时,把任务控制块归还空任务控制块链表,如下所示:
在这里插入图片描述

static  void  OS_InitTCBList (void)
{
    INT8U    ix;
    INT8U    ix_next;
    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           */
    for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) 
	{                                                            /* Init. list of free TCBs     */
        ix_next =  ix + 1u;
        ptcb1   = &OSTCBTbl[ix];
        ptcb2   = &OSTCBTbl[ix_next];
        ptcb1->OSTCBNext = ptcb2;
#if OS_TASK_NAME_EN > 0u
        ptcb1->OSTCBTaskName = (INT8U *)(void *)"?";             /* Unknown name                       */
#endif
    }
    ptcb1                   = &OSTCBTbl[ix];
    ptcb1->OSTCBNext        = (OS_TCB *)0;                       /* Last OS_TCB                        */
#if OS_TASK_NAME_EN > 0u
    ptcb1->OSTCBTaskName    = (INT8U *)(void *)"?";              /* Unknown name                       */
#endif
    OSTCBList               = (OS_TCB *)0;                       /* TCB lists initializations  将任务控制块列表进行初始化        */
    OSTCBFreeList           = &OSTCBTbl[0];                      /*将TCB空闲列表的指针指向TCB表的首地址*/
}

3任务创建

任务创建的过程,实际就是向任务控制块中添加内容的过程,以空任务和统计任务为例 :
创建空任务:
1,在空任务块链表中取出一个,即空链表的表头下移一个,OSTCBFreeList指向下一个空任务块,永远指向空链表的表头。
2,然后把创建的任务的相关参数,放到取出的空任务控制块内;
3,把放置任务的控制块加入到任务控制块链表中,OSTCBList指向任务控制块双向链表的表头
创建统计任务:
1,在空任务链表中取出一个空任务块,OSTCBFreeList指向下一个空任务块。
2,把统计任务的相关信息,放到取出的空任务块内;
3,把该任务的控制块,加入到双向任务控制块链表内,OSTCBList指向任务控制块双向链表的表头

   ptcb->OSTCBNext    = OSTCBList;                    /* ptcb当前新创建的任务,这句话的意思是当前任务的下一个指向原先的表头,即新建任务放置在表头处       */
    ptcb->OSTCBPrev    = (OS_TCB *)0;                  /*当前新创建任务的上一个指向0,因为新创建任务放置在表头,已经是最前面了*/
    if (OSTCBList != (OS_TCB *)0)                          //如果不是首次创建任务
	{
        OSTCBList->OSTCBPrev = ptcb;                   //新建任务插入到表头,所以原先表头任务后移一个,它的前一个指向新建任务,建立双向链表
    }

在这里插入图片描述

4任务优先级就绪表

任务优先级就绪表的作用快速找到就绪任务对应的优先级,每个任务对应一个优先级,如何通过优先级判断任务的就绪状态呢,主要通过两个表格:优先级就绪表和优先级判定表。

  • 优先级就绪表
    我们知道ucos操作系统是一个优先级抢占式多任务系统,总共有64个优先级,0-63,数字越小优先级越高,高优先级任务可以抢占低优先级任务执行,即在任务调度过程中,总是执行优先级最高的任务。系统如何知道那个任务处于就绪状态,且优先级最高呢,就是我们通常说的优先级就绪表。优先级就绪表是一个8个8位的数组,总共有64位,每一位代表一个优先级的状态,如下表所示。
    在这里插入图片描述
    对上图解释:优先级最大63,二进制表示0011 1111,即最高位是用不到的,仅用到了低6位,高三位和低三位取值范围位0-7,用高三位表示行,低三位表示列,就构成了如上对应图。当需要将某个优先级置位就绪状态时 ,将相应的位置1即可,比如:优先级23 二进制表示:0001 0111 高三位010 即第二行,低三位111,第七列。

为了更清晰的表示就绪表中某行某列有就绪任务,引入映射表,如下:
在这里插入图片描述
该映射的意思:第几行有就绪任务,第几位置1,同理,第几列有任务,第几位置1。

既然知道了就绪表的原理,那代码中如何实现的呢,为了更清楚代码的执行过程,且先熟悉下程序中两个变量:
OSRdyGrp:优先级继续组,该变量是8位的,每一位都对应上面的一行,哪位为1就表示哪行有就绪的任务。
OSRdyTbl[]:优先级就绪表,知道了某行有就绪任务后,可以根据此数组判断具体是那列有就绪任务。该行有几个1就代表有几个就绪任务。
具体代码实现入下:先确定那行那列,然后写入就绪表中

#if OS_LOWEST_PRIO <= 63u                                         /* Pre-compute X, Y   预处理任务优先级 */
        ptcb->OSTCBY             = (INT8U)(prio >> 3u);
        ptcb->OSTCBX             = (INT8U)(prio & 0x07u);
#else                                                             /* Pre-compute X, Y                  */
        ptcb->OSTCBY             = (INT8U)((INT8U)(prio >> 4u) & 0xFFu);
        ptcb->OSTCBX             = (INT8U) (prio & 0x0Fu);
#endif
                                                                  /* Pre-compute BitX and BitY         */
        ptcb->OSTCBBitY          = (OS_PRIO)(1uL << ptcb->OSTCBY);
        ptcb->OSTCBBitX          = (OS_PRIO)(1uL << ptcb->OSTCBX);该处的作用就是求映射关系,处于第几行第几列,就把字节中的第几位置1


OSRdyGrp               |= ptcb->OSTCBBitY;         /*  更新就绪表    使任务进入"就绪"状态            */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
  • 优先级判定表
    上面了解了优先级就绪表的原理,那如何根据优先级就绪表快速的计算出具体优先级呢,这里引入优先级判定表。

     INT8U  const  OSUnMapTbl[256] = {
         0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */
         5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */
         6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */
         5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */
         7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */
         5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */
         6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */
         5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */
         4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
     };
    

    代码的实现:

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

OSRdyGrp:优先级就绪组,那位为1代表哪行有就绪任务,取值范围0000 0000-1111 1111(0-255),最大时表示所有行都有任务。因为上面映射的时候说过,哪行有任务,哪位就置1,则现在则反过来,OSUnMapTbl[OSRdyGrp ] 根据哪位是1,求出在哪行,这里求出的是最高优先级所在的行。
举例说明:
优先级23 二进制00 010 110
根据映射关系,高三位代表行,0X010=2,则在第三行(从0开始),则映射后为OSRdyGrp=0000 0100
根据判定表OSUnMapTbl[OSRdyGrp ] =OSUnMapTbl[4 ] =2,即010。

根据映射关系,低三位代表列,0X110=6,则在第六列(从0开始),映射后为OSRdyTbl[2]=0100 0000
根据判定表OSUnMapTbl[OSRdyTbl[2] ] =OSUnMapTbl[64 ] =6,即110。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值