- 简述:
- 在uC/OS中,任务被创建后,任务的TCB会被放入就绪列表中,表示任务己就绪,随时可能被运行;
- 就绪列表包含2部分:
- 任务优先级的优先级表;
- 存储任务TCB的双向链表;
- 优先级表:
- 优先级表就是一个类型为INT32U的数组OSPrioTbl[];
- CPU_DATA决定支持多少个优先级(依据CPU类型决定,8、16、32),改成相应的位数;
- 它的每一个二进制位对应一个任务;
- 该位的值为1表示对应的任务处于就绪状态,为0表示对应的任务处于非就绪状态;
- 优先级表中0为最高优级级;
- 按照从高到低的查找方式称为:前导0函数CPU_CntLeadZeros();
- ---------低---高---------------------:后----------
- 如果使用的处理器支持前导零指令CLZ,可以汇编实现,加快指令运算;
- 处理器不支持汇编需要用C来编写,需要定义个位图;
- 位图本质上是个数组表,通过查表得到前导0个数;
- 定义位图表CPU_CntLeadZerosTb1[]这个表只是浪费点内存空间,但可以达到空间换时间的目的;
- 例程:
- 初始化优先级表;
/********************************************************* * 函数名: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; } }
- 设置优先级表中相应的位;
/********************************************************* * 函数名: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; }
- 清除优先级表中相应的位;
/********************************************************* * 函数名: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; }
- 查找最高的优先级;
/********************************************************* * 函数名: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); }
- 初始化优先级表;
- 就绪列表
- 就绪表本质也是个OS_RDY_LIST数据类型的数组OSRdyList[];
- 数组大小由宏OS_CFG_PRIO_MAX决定,支持多少个优先级,OSRdyList[]就有多少个成员;
- 系统总是从处于就绪状态的任务中来选择一个任务运行;
- 就绪表是用双向链表实现;
- 就绪表的函数与链表相同,增、删、改、查;
- 例程
- 初始化就绪列表为空
/********************************************************* * 函数名: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; } }
- 插入一个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); } }
- 插入一个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; } }
- 插入一个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; } }
- 将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; } }
- 将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; }
- 初始化就绪列表为空
- 总结:
- 理解双向链表、位图、空间换时间、前导0、后导0、优先级等诸多概念;
【16】从0到1教你写uC/OS-III————>就绪列表
最新推荐文章于 2022-11-08 00:09:57 发布