【16】从0到1教你写uC/OS-III————>就绪列表

  1. 简述:
    1. 在uC/OS中,任务被创建后,任务的TCB会被放入就绪列表中,表示任务己就绪,随时可能被运行;
    2. 就绪列表包含2部分:
      1. 任务优先级的优先级表;
      2. 存储任务TCB的双向链表;
  2. 优先级表:
    1. 优先级表就是一个类型为INT32U的数组OSPrioTbl[];
    2. CPU_DATA决定支持多少个优先级(依据CPU类型决定,8、16、32),改成相应的位数;
    3. 它的每一个二进制位对应一个任务;
    4. 该位的值为1表示对应的任务处于就绪状态,为0表示对应的任务处于非就绪状态;
    5. 优先级表中0为最高优级级;
    6. 按照从高到低的查找方式称为:前导0函数CPU_CntLeadZeros();
    7. ---------低---高---------------------:后----------
    8. 如果使用的处理器支持前导零指令CLZ,可以汇编实现,加快指令运算;
    9. 处理器不支持汇编需要用C来编写,需要定义个位图;
    10. 位图本质上是个数组表,通过查表得到前导0个数;
    11. 定义位图表CPU_CntLeadZerosTb1[]这个表只是浪费点内存空间,但可以达到空间换时间的目的; 
  3. 例程:
    1. 初始化优先级表;
      /*********************************************************
       * 函数名:void OS_PrioInit(void);
       * 描述  :初始化优先级表
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_PrioInit(void)
      {
          CPU_DATA    i;
          
          //默认全部初始化为0
          for(i = 0u; i < OS_PRIO_TBL_SIZE; i++)
          {
              OSPrioTbl[i] = (CPU_DATA)0;
          }
      }
      

       

    2. 设置优先级表中相应的位;
      /*********************************************************
       * 函数名:void OS_PrioInser(OS_PRIO   prio);
       * 描述  :设置优先级表中相应的位
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_PrioInser(OS_PRIO   prio)
      {
          CPU_DATA    bit;
          CPU_DATA    bit_nbr;
          OS_PRIO     ix;
          
          //求模操作,获取优先级表数组的下标索引
          ix = prio / DEF_INT_CPU_NBR_BITS;
          
          //求余操作,将优先级限制在DEF_INT_CPU_NBR_BITS之内
          bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);
          
          //获取优先级在优先级表中对应的位的位置
          bit = 1u;
          bit <<= ((DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr);
          
          //将优先级在优先级表中对应的位置1
          OSPrioTbl[ix] |= bit;
      }

       

    3. 清除优先级表中相应的位;
      /*********************************************************
       * 函数名:void OS_PrioRemov(OS_PRIO   prio);
       * 描述  :清除优先级表中相应的位
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_PrioRemov(OS_PRIO   prio)
      {
          CPU_DATA    bit;
          CPU_DATA    bit_nbr;
          OS_PRIO     ix;
          
          //求模操作,获取优先级表数组的下标索引
          ix = prio / DEF_INT_CPU_NBR_BITS;
          
          //求余操作,将优先级限制在DEF_INT_CPU_NBR_BITS之内
          bit_nbr = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);
          
          //获取优先级在优先级表中对应的位的位置
          bit = 1u;
          bit <<= ((DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr);
          
          //将优先级在优先级表中对应的位置1
          OSPrioTbl[ix] &= ~bit;
      }
      

       

    4. 查找最高的优先级;
      /*********************************************************
       * 函数名:OS_PRIO OS_PrioGetHighest(void);
       * 描述  :查找最高的优先级
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      OS_PRIO OS_PrioGetHighest(void)
      {
          CPU_DATA    *p_tbl;
          OS_PRIO     prio;
          
          prio = (OS_PRIO)0;
          //获取优先级表首地址
          p_tbl = &OSPrioTbl[0];
          
          //找不到数值不为0的数组成员
          while(*p_tbl == (CPU_DATA)0)
          {
              prio += DEF_INT_CPU_NBR_BITS;
              p_tbl++;
          }
          
          //找到优先级表中己置位的最高优先级
          prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);
          return (prio);
      }

       

  4. 就绪列表
    1. 就绪表本质也是个OS_RDY_LIST数据类型的数组OSRdyList[];
    2. 数组大小由宏OS_CFG_PRIO_MAX决定,支持多少个优先级,OSRdyList[]就有多少个成员;
    3. 系统总是从处于就绪状态的任务中来选择一个任务运行;
    4. 就绪表是用双向链表实现;
    5. 就绪表的函数与链表相同,增、删、改、查;
  5. 例程
    1. 初始化就绪列表为空
       /*********************************************************
       * 函数名:void OS_RdyListInit(void);                    
       * 描述  :初始化就绪列表为空
      *          OS_RDY_LIST  OSRdyTbl[OS_CFG_PRIO_MAX]'  初始化之后就像下面这样:
      *
      *                               +---------------+--------------+
      *                               |               | TailPtr      |-----> 0
      *                          [0]  | NbrEntries=0  +--------------+
      *                               |               | HeadPtr      |-----> 0
      *                               +---------------+--------------+
      *                               |               | TailPtr      |-----> 0
      *                          [1]  | NbrEntries=0  +--------------+
      *                               |               | HeadPtr      |-----> 0
      *                               +---------------+--------------+
      *                                       :              :
      *                                       :              :
      *                                       :              :
      *                               +---------------+--------------+
      *                               |               | TailPtr      |-----> 0
      *         [OS_CFG_PRIO_MAX-1]  | NbrEntries=0  +--------------+
      *                               |               | HeadPtr      |-----> 0
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListInit(void)
      {
           OS_PRIO    i;
           OS_RDY_LIST    *p_rdy_list;
           
           for(i = 0u; i < OS_CFG_PRIO_MAX; i++)
           {
               p_rdy_list = &OSRdyList[i];        //32级任务优先级
               p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;
               p_rdy_list->HeadPtr = (OS_TCB *)0;
               p_rdy_list->TailPtr = (OS_TCB *)0;
           } 
      }
      

       

    2. 插入一个TCB到就绪列表
      /*********************************************************
       * 函数名:void OS_RdyListInsert(OS_TCB    *p_tcb);                  
       * 描述  :插入一个TCB到就绪列表
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListInsert(OS_TCB    *p_tcb)
      {
          //将优先级插入到优先级表
          OS_PrioInsert(p_tcb->Prio);
          
          if(p_tcb->Prio == OSPrioCur)
          {
              //如果是当前优先级则插入到链表尾部
              OS_RdyListInsertTail(p_tcb);
          }
          else
          {
              //插入到链表头部
              OS_RdyListInsertHead(p_tcb);
          }
      }

       

    3. 插入一个TCB到就绪列表的头部
      /*********************************************************
       * 函数名:void OS_RdyListInsertHead(OS_TCB    *p_tcb);                
       * 描述  :插入一个TCB到就绪列表的头部
      	*  在链表头部插入一个TCB有以下两种情况
      	*              CASE 0: 插入到一个空的链表
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-> 0
      	*                     +--------------+
      	*                     | HeadPtr      |-> 0
      	*                     +--------------+
      	*                     | NbrEntries=0 |
      	*                     +--------------+
      	*
      	*
      	*
      	*              CASE 1: 链表已经有节点
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+          OS_TCB
      	*                     | TailPtr      |--+---> +------------+
      	*                     +--------------+  |     | NextPtr    |->0
      	*                     | HeadPtr      |--/     +------------+
      	*                     +--------------+     0<-| PrevPtr    |
      	*                     | NbrEntries=1 |        +------------+
      	*                     +--------------+        :            :
      	*                                             :            :
      	*                                             +------------+
      	*
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-----------------------------------------------+
      	*                     +--------------+          OS_TCB               OS_TCB          |     OS_TCB
      	*                     | HeadPtr      |------> +------------+       +------------+    +-> +------------+
      	*                     +--------------+        | NextPtr    |------>| NextPtr    | ...... | NextPtr    |->0
      	*                     | NbrEntries=N |        +------------+       +------------+        +------------+
      	*                     +--------------+     0<-| PrevPtr    |<------| PrevPtr    | ...... | PrevPtr    |
      	*                                             +------------+       +------------+        +------------+
      	*                                             :            :       :            :        :            :
      	*                                             :            :       :            :        :            :
      	*                                             +------------+       +------------+        +------------+
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListInsertHead(OS_TCB    *p_tcb)
      {
          OS_RDY_LIST *p_rdy_list;
          OS_TCB  *p_tcb2;
          
          //获取链表根部
          p_rdy_list = &OSRdyList[p_tcb->Prio];
          
          //情况1:链表为空
          if(p_rdy_list->NbrEntries == (OS_OBJ_QTY)0)
          {
              p_rdy_list->NbrEntries = (OS_OBJ_QTY)1;
              p_tcb->NextPtr = (OS_TCB    *)0;
              p_tcb->PrevPtr = (OS_TCB    *)0;
              p_rdy_list->HeadPtr = p_tcb;
              p_rdy_list->TailPtr = p_tcb;
          }
          //情况2:链表已有节点
          else
          {
              p_rdy_list->NbrEntries++;
              p_tcb->NextPtr = p_rdy_list->HeadPtr;
              p_tcb2->PrevPtr = p_tcb;
              p_rdy_list->HeadPtr = p_tcb;        
          }
      }
      

       

    4. 插入一个TCB到就绪列表的尾部
      /*********************************************************
       * 函数名:void OS_RdyListInsertTail(OS_TCB    *p_tcb);               
       * 描述  :插入一个TCB到就绪列表的尾部
      	*  在链表尾部插入一个TCB有以下两种情况
      	*              CASE 0: 插入到一个空的链表
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-> 0
      	*                     +--------------+
      	*                     | HeadPtr      |-> 0
      	*                     +--------------+
      	*                     | NbrEntries=0 |
      	*                     +--------------+
      	*
      	*
      	*
      	*              CASE 1: 链表已经有节点
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+          OS_TCB
      	*                     | TailPtr      |--+---> +------------+
      	*                     +--------------+  |     | NextPtr    |->0
      	*                     | HeadPtr      |--/     +------------+
      	*                     +--------------+     0<-| PrevPtr    |
      	*                     | NbrEntries=1 |        +------------+
      	*                     +--------------+        :            :
      	*                                             :            :
      	*                                             +------------+
      	*
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-----------------------------------------------+
      	*                     +--------------+          OS_TCB               OS_TCB          |     OS_TCB
      	*                     | HeadPtr      |------> +------------+       +------------+    +-> +------------+
      	*                     +--------------+        | NextPtr    |------>| NextPtr    | ...... | NextPtr    |->0
      	*                     | NbrEntries=N |        +------------+       +------------+        +------------+
      	*                     +--------------+     0<-| PrevPtr    |<------| PrevPtr    | ...... | PrevPtr    |
      	*                                             +------------+       +------------+        +------------+
      	*                                             :            :       :            :        :            :
      	*                                             :            :       :            :        :            :
      	*                                             +------------+       +------------+        +------------+
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListInsertTail(OS_TCB    *p_tcb)
      {
          OS_RDY_LIST *p_rdy_list;
          OS_TCB *p_tcb2;
          
          //获取链表根部
          p_rdy_list = &OSRdyList[p_tcb->Prio];
          
          //情况1:链表为空
          if(p_rdy_list->NbrEntries == (OS_OBJ_QTY)0)
          {
              p_rdy_list->NbrEntries = (OS_OBJ_QTY)1;
              p_tcb->NextPtr = (OS_TCB    *)0;
              p_tcb->PrevPtr = (OS_TCB    *)0;
              p_rdy_list->HeadPtr = p_tcb;
              p_rdy_list->TailPtr = p_tcb;
          }
          //情况2:链表己有节点
          else
          {
              p_rdy_list->NbrEntries++;
              p_tcb->NextPtr  =   (OS_TCB *)0;
              p_tcb2  =   p_rdy_list->TailPtr;
              p_tcb->PrevPtr  =   p_tcb2;
              p_tcb2->NextPtr =   p_tcb;
              p_rdy_list->TailPtr =   p_tcb;
          }
      }

       

    5. 将TCB从就绪列表的头部移到尾部
      /*********************************************************
       * 函数名:void OS_RdyListInsertTail(OS_TCB    *p_tcb);               
       * 描述  :将TCB从就绪列表的头部移到尾部
      	*  将链表中的节点从头部移动到尾部,包含下面三种情况:
      	*              CASE 0: 链表为空,无事可做。
      	*
      	*              CASE 1: 链表只有一个节点,无事可做。
      	*
      	*              CASE 2: 链表只有两个节点。
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |--------------------------+
      	*                     +--------------+          OS_TCB          |     OS_TCB
      	*                     | HeadPtr      |------> +------------+    +-> +------------+
      	*                     +--------------+        | NextPtr    |------> | NextPtr    |->0
      	*                     | NbrEntries=2 |        +------------+        +------------+
      	*                     +--------------+     0<-| PrevPtr    | <------| PrevPtr    |
      	*                                             +------------+        +------------+
      	*                                             :            :        :            :
      	*                                             :            :        :            :
      	*                                             +------------+        +------------+
      	*
      	*
      	*              CASE N: 链表有两个节点以上。
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-----------------------------------------------+
      	*                     +--------------+          OS_TCB               OS_TCB          |     OS_TCB
      	*                     | HeadPtr      |------> +------------+       +------------+    +-> +------------+
      	*                     +--------------+        | NextPtr    |------>| NextPtr    | ...... | NextPtr    |->0
      	*                     | NbrEntries=N |        +------------+       +------------+        +------------+
      	*                     +--------------+     0<-| PrevPtr    |<------| PrevPtr    | ...... | PrevPtr    |
      	*                                             +------------+       +------------+        +------------+
      	*                                             :            :       :            :        :            :
      	*                                             :            :       :            :        :            :
      	*                                             +------------+       +------------+        +------------+
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListMoveHeadToTail(OS_RDY_LIST *p_rdy_list)
      {
          OS_TCB  *p_tcb1;
          OS_TCB  *p_tcb2;
          OS_TCB  *p_tcb3;
          
          switch(p_rdy_list->NbrEntries)
          {
              case 0:
              case 1:
                  break;
              case 2:
                      p_tcb1          =   p_rdy_list->HeadPtr;
                      p_tcb2          =   p_rdy_list->TailPtr;
                      p_tcb1->PrevPtr =   p_tcb2;
                      p_tcb1->NextPtr =   (OS_TCB *)0;
                      p_tcb2->PrevPtr =   (OS_TCB *)0;
                      p_tcb2->NextPtr =   p_tcb1;
                      
                      p_rdy_list->HeadPtr =   p_tcb2;
                      p_rdy_list->TailPtr =   p_tcb1;
                  break;
              default:
                      p_tcb1          =   p_rdy_list->HeadPtr;
                      p_tcb2          =   p_rdy_list->TailPtr;
                      p_tcb3          =   p_tcb1->NextPtr;
              
                      p_tcb3->PrevPtr =   (OS_TCB *)0;
                      p_tcb1->NextPtr =   (OS_TCB *)0;
                      p_tcb1->PrevPtr =   p_tcb2;
                      p_tcb2->NextPtr =   p_tcb1;
              
                      p_rdy_list->HeadPtr =   p_tcb3;
                      p_rdy_list->TailPtr =   p_tcb1;
                  break;
          }
      }
      

       

    6. 将TCB从就绪列表中移除
      /*********************************************************
       * 函数名:void OS_RdyListRemove(OS_TCB    *p_tcb);             
       * 描述  :将TCB从就绪列表中移除 
      	*  从链表中移除一个TCB有以下几种情况:
      	*              CASE 0: 链表为空,无事可做。
      	*
      	*              CASE 1: 链表只有一个节点。
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+          OS_TCB
      	*                     | TailPtr      |--+---> +------------+
      	*                     +--------------+  |     | NextPtr    |->0
      	*                     | HeadPtr      |--/     +------------+
      	*                     +--------------+     0<-| PrevPtr    |
      	*                     | NbrEntries=1 |        +------------+
      	*                     +--------------+        :            :
      	*                                             :            :
      	*                                             +------------+
      	*
      	*              CASE N: 链表中有两个以上的节点。
      	*
      	*                     OS_RDY_LIST
      	*                     +--------------+
      	*                     | TailPtr      |-----------------------------------------------+
      	*                     +--------------+          OS_TCB               OS_TCB          |     OS_TCB
      	*                     | HeadPtr      |------> +------------+       +------------+    +-> +------------+
      	*                     +--------------+        | NextPtr    |------>| NextPtr    | ...... | NextPtr    |->0
      	*                     | NbrEntries=N |        +------------+       +------------+        +------------+
      	*                     +--------------+     0<-| PrevPtr    |<------| PrevPtr    | ...... | PrevPtr    |
      	*                                             +------------+       +------------+        +------------+
      	*                                             :            :       :            :        :            :
      	*                                             :            :       :            :        :            :
      	*                                             +------------+       +------------+        +------------+
       * 输入  :无
       * 输出  :无
       * 返回  :无 
       * 调用  :内部调用 
       **********************************************************/
      void OS_RdyListRemove(OS_TCB    *p_tcb)
      {
          OS_RDY_LIST *p_rdy_list;
          OS_TCB  *p_tcb1;
          OS_TCB  *p_tcb2;
          
          p_rdy_list = &OSRdyList[p_tcb->Prio];
          
          //保存要删除的TCB节点的前一个和后一个节点
          p_tcb1  =   p_tcb->PrevPtr;
          p_tcb2  =   p_tcb->NextPtr;
          
          //要移除的TCB节点是链表中的第一个节点
          if(p_tcb1 == (OS_TCB *)0)
          {
              //且该链表中只有一个节点
              if(p_tcb2 == (OS_TCB *)0)
              {
                  //根节点全部初始化为0
                  p_rdy_list->NbrEntries  =   (OS_OBJ_QTY)0;
                  p_rdy_list->HeadPtr     =   (OS_TCB *)0;
                  p_rdy_list->TailPtr     =   (OS_TCB *)0;
                  
                  //清除在优先级表中相应的位
                  OS_PrioRemov(p_tcb->Prio);
              }
              //该链表中不止一个节点
              else
              {
                  //节点减1
                  p_rdy_list->NbrEntries--;
                  p_tcb2->PrevPtr =   (OS_TCB *)0;
                  p_rdy_list->HeadPtr =   p_tcb2;
              }
          }
          //要移除的TCB节点不是链表中的第一个节点
          else
          {
              p_rdy_list->NbrEntries--;
              p_tcb1->NextPtr = p_tcb2;
              
              //如果要删除的节点的下一个节点是0,即要删除的节点是最后一个节点
              if(p_tcb2 == (OS_TCB *)0)
              {
                  p_rdy_list->TailPtr = p_tcb1;
              }
              else
              {
                  p_tcb2->PrevPtr =   p_tcb1;
              }
          }
          //复位从就绪列表中删除TCB的PrePtr和NextPtr这两个指针
          p_tcb->PrevPtr = (OS_TCB *)0;
          p_tcb->NextPtr = (OS_TCB *)0;
      }

       

  6. 总结:
    1. 理解双向链表、位图、空间换时间、前导0、后导0、优先级等诸多概念;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值