μC/OS III - 任务调度 Ⅱ:任务列表

这是μC/OS III任务调度的第二篇文章:任务列表。分析μC/OS III的三种任务列表:就绪列表,挂起列表,时基列表。并分析这三种任务列表的存在方式、作用、操作函数等。

一. 就绪列表Ready List

就绪列表里放的是准备(可以)运行的任务。就绪列表由两部分组成:优先级位图(BitMap)指向所有就绪任务的列表

1. 任务优先级和优先级位图

任务优先级的最大值在os_cfg.h中有定义:

[os_cfg.h]

#define OS_CFG_PRIO_MAX      64u

任务优先级的数据类型是一个有符号字节类型:

[os_type.h]

typedef   unsigned  char   CPU_INT08U;
typedef   CPU_INT08U       OS_PRIO;

优先级位图是一个数组OSPrioTbl[]

[os_prio.c]

CPU_DATA   OSPrioTbl[OS_PRIO_TBL_SIZE];

其中OS_PRIO_TBL_SIZE宏定义如下:

[lib_def.h]

#define  CPU_WORD_SIZE_32       4u
#define  CPU_CFG_DATA_SIZE      CPU_WORD_SIZE_32
#define  DEF_OCTET_NBR_BITS     8u
#define  DEF_INT_CPU_NBR_BITS   (CPU_CFG_DATA_SIZE * DEF_OCTET_NBR_BITS)

[os.h]

#define  OS_PRIO_TBL_SIZE  (((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS)) + 1u)

其含义指的是OSPrioTbl[]的长度最少有OS_CFG_PRIO_MAXBit。如此便把所有的优先级都用一个Bit进行了映射,映射到OSPrioTbl[]数组相应的Bit上。

获取就绪任务列表中任务的最高优先级函数为OS_PrioGetHighest

[os_prio.c]

OS_PRIO  OS_PrioGetHighest (void)
{
    CPU_DATA  *p_tbl;
    OS_PRIO    prio;

    prio  = 0u;
    p_tbl = &OSPrioTbl[0];
#if (OS_CFG_PRIO_MAX > DEF_INT_CPU_NBR_BITS)
    while (*p_tbl == 0u) {
        prio += (OS_PRIO)DEF_INT_CPU_NBR_BITS;
        p_tbl++;
    }
#endif

    /* Find the position of the first bit set at the entry  */
    prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);

    return (prio);
}

此函数就是获取OSPrioTbl[]数组中第一个不为0的Bit的位序号。

和优先级位图相关的函数有4个:

序号函数作用
1OS_PrioInit(void)OSPrioTbl[] 所有成员都清0
2OS_PrioInsert(OS_PRIO prio)OSPrioTbl[]prio 优先级所对应的位设1
3OS_PrioRemove(OS_PRIO prio)OSPrioTbl[]prio 优先级所对应的位清0
4OS_PrioGetHighest(void)获取就绪列表中最高的任务优先级

2. 指向所有就绪任务的列表

指向所有就绪任务的指针列表是用数组OSRdyList来表示:

[os.h]

struct  os_rdy_list {

    OS_TCB  *HeadPtr;
    OS_TCB  *TailPtr;

#if (OS_CFG_DBG_EN == DEF_ENABLED)
    OS_OBJ_QTY           NbrEntries;
#endif
};

typedef  struct  os_rdy_list  OS_RDY_LIST;

OS_RDY_LIST  OSRdyList[OS_CFG_PRIO_MAX];

这个数组的长度就是最大优先级的大小,数组中每一个成员代表一个优先级,其值是指向包含此优先级中所有的任务的一个链表。

和优先级位图相关的函数有6个:

序号函数作用
1OS_RdyListInit(void)初始化 OSRdyList, 把每一个 OS_RDY_LISTHeadPtrTailPtr 全设位0。
2OS_RdyListInsert(OS_TCB *p_tcb)把一个任务插入 OSRdyList, 先调用 OS_PrioInsert 插入设置优先级的 BitMap, 如果任务 p_tcb 的优先级和当前任务的优先级相同, 那么调用 OS_RdyListInsertTail 把这个任务插入到相应链表的尾部; 如果不同, 那么调用 OS_RdyListInsertHead, 把这个任务插入到相应链表的头部。
3OS_RdyListInsertHead(OS_TCB *p_tcb)把一个任务插入 OSRdyList 中某个与其优先级相对应的链表的头部。
4OS_RdyListInsertTail(OS_TCB *p_tcb)把一个任务插入 OSRdyList 中某个与其优先级相对应的链表的尾部。
5OS_RdyListMoveHeadToTail(OS_RDY_LIST *p_rdy_list)把一个链表的头部的 OS_TCB 移动到尾部。
6OS_RdyListRemove(OS_TCB *p_tcb)把一个任务 p_tcb 从包含这个任务的链表中移除, 即把这个任务移除就绪列表OSRdyList

二. 挂起列表Pend List

1. 挂起列表的定义

挂起列表的定义在os.h里:

[os.h]

typedef  struct  os_pend_list  OS_PEND_LIST;

struct  os_pend_list {
    OS_TCB              *HeadPtr;
    OS_TCB              *TailPtr;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    OS_OBJ_QTY           NbrEntries;
#endif
};

其定义和就绪列表Ready List几乎相同。

2. 挂起列表的操作函数

挂起列表Pend List有4个操作函数:

序号函数作用
1OS_PendListInit(OS_PEND_LIST *p_pend_list)初始化一个 Pend List,把 HeadPtrTailPtr 设置为0。
2OS_PendListInsertPrio(OS_PEND_LIST *p_pend_list, OS_TCB *p_tcb)把一个任务插入到一个 Pend List 中, 需要注意的是, Pend List 中任务的顺序是按照优先级大小排列的, 优先级最高的任务在前(即Prio值最小的任务在前)。
3OS_PendListChangePrio(OS_TCB *p_tcb)如果一个任务的优先级发生了变化, 并且这个任务在 Pend List中, 调用这个函数可以根据新的优先级来改变这个任务在这个 Pend List 中的位置。
4OS_PendListRemove(OS_TCB *p_tcb)把一个任务 p_tcb 从他所属的 Pend List 中移除。

3. 挂起列表与内核对象的关系

Pend List存在于多个内核对象中:

序号Pend List的内核对象意义
1OS_FLAG_GRP事件标志组
2OS_MUTEX互斥信号量
3OS_Q消息队列
4OS_SEM计数信号量
5OS_MON监视器

其中OS_FLAG_GRP的定义如下:

[os.h]

struct  os_flag_grp {

#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    ...
};

typedef  struct  os_flag_grp  OS_FLAG_GRP;

OS_MUTEX的定义如下:

[os.h]

struct  os_mutex {

#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    ...
};

typedef  struct  os_mutex  OS_MUTEX;

OS_Q的定义如下:

[os.h]

struct  os_q {

#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    ...
};

typedef  struct  os_q  OS_Q;

OS_SEM的定义如下:

[os.h]

struct  os_sem {

#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif

    ...
};

typedef  struct  os_sem  OS_SEM;

OS_MON的定义如下:

[os.h]

struct  os_mon {

#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
    ...
};

typedef  struct  os_mon  OS_MON;

可以看出他们都包含一个挂起列表OS_PEND_LIST,并且他们的定义的前部分都是相同的。μC/OS III这样子做是为了他们结构体有一个相同的部分,操作这些内核对象的时候可以类似操作c++ Class的父类一样,用同样的接口去操作他们。他们的父类的定义为OS_PEND_OBJ,其定义如下:

[os.h]

struct  os_pend_obj {
#if (OS_OBJ_TYPE_REQ == DEF_ENABLED)
    OS_OBJ_TYPE          Type;
#endif
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    CPU_CHAR            *NamePtr;
#endif
    OS_PEND_LIST         PendList;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    void                *DbgPrevPtr;
    void                *DbgNextPtr;
    CPU_CHAR            *DbgNamePtr;
#endif
};

typedef  struct  os_pend_obj   OS_PEND_OBJ;

在代表一个任务实体的结构体OS_TCB中也有一个OS_PEND_OBJ来记录当前任务属于哪个OS_PEND_OBJ

[os.h]

struct os_tcb {
    ...
    OS_TCB              *PendNextPtr;
    OS_TCB              *PendPrevPtr;
    OS_PEND_OBJ         *PendObjPtr;
    OS_STATE             PendOn;
    OS_STATUS            PendStatus;
    ...
};

4. 挂起列表的作用

每当一个任务在Pend某一个资源(内核对象)的时候,μC/OS III会先把这个任务从就绪列表中移除,再把这个任务加入到这个资源(内核对象)的挂起列表中。等到其他任务或者中断对这个资源进行Post操作时,μC/OS III会判断正在等待的任务是否满足条件,如果满足条件就把这个任务从挂起列表中移除,再次放入到就绪列表中。

三. 时基列表

1. 时基列表的定义

struct  os_tick_list {
    OS_TCB              *TCB_Ptr;
#if (OS_CFG_DBG_EN == DEF_ENABLED)
    OS_OBJ_QTY           NbrEntries;
    OS_OBJ_QTY           NbrUpdated;
#endif
};

typedef  struct  os_tick_list  OS_TICK_LIST;

2. 时基列表的操作函数

时基列表Tick List有3个操作函数:

序号函数作用
1OS_TickListInsert(OS_TICK_LIST *p_list, OS_TCB *p_tcb, OS_TICK time)把指定的任务插入到指定的 Tick List 中, 并设置这个任务的超时时间, 这个时间是以一个 Tick 为单位的,插入到 Tick List 链表中的任务的排列顺序是以 Deltac Time 为依据的,最前面的任务的RemainTime是距离当前时间的差值, 后面链表的每一个任务都是距离其前面的任务的RemainTime的差值。
2OS_TickListInsertDly(OS_TCB *p_tcb, OS_TICK time, OS_OPT opt, OS_ERR *p_err)是把指定的任务插入到OSTickListDly 中, 最终调用 OS_TickListInsert
3OS_TickListRemove(OS_TCB *p_tcb)把指定的任务从其所在的 Tick List 中移除。

3. 时基列表的存在方式及其作用

时基列表有两个,在os.h中有定义:

[os.h]

OS_EXT  OS_TICK_LIST  OSTickListDly;
OS_EXT  OS_TICK_LIST  OSTickListTimeout;
  1. OSTickListDly是为μC/OS III提供一种延时的机制,当任务调用OSTimeDly或者OSTimeDlyHMSM的时候,μC/OS III就会把调用这两个函数的任务放入到OSTickListDly时基列表中,每当来一次SysTick的时候,在Tick Task中会被更新剩余的延时时间。
  2. OSTickListTimeout为一些Pend操作提供Timeout的机制,任务在Pend操作等待一些资源的时候,会把任务挂到OSTickListTimeout时基列表里,最终在Tick Task中处理。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值