(学习日记)2024.03.09:UCOSIII第十一节:就绪列表

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.03.09

  • 二十四、UCOSIII:就绪列表
    • 1、优先级表
    • 2、 优先级表函数讲解
        • 1. OS_PrioInit()函数
        • 2. OS_PrioInsert()函数
        • 3. OS_PrioRemove()函数
        • 4. OS_PrioGetHighest()函数
    • 3、就绪列表
    • 4、就绪列表函数讲解
      • 1. OS_RdyListInit()函数
      • 2. OS_RdyListInsertHead()函数
      • 3. OS_RdyListInsertTail()函数
      • 4. OS_RdyListInsert()函数
      • 5. OS_RdyListMoveHeadToTail()函数
      • 6. OS_RdyListRemove()函数

二十四、UCOSIII:就绪列表

在μC/OS-III中,任务被创建后,任务的TCB会被放入就绪列表中,表示任务在就绪,随时可能被运行。 就绪列表包含一个表示任务优先级的优先级表,一个存储任务TCB的TCB双向链表。

1、优先级表

优先级表在代码层面上来看,就是一个数组,在文件os_prio.c(Source中新建)的开头定义

/* 定义优先级表,在os.h中用extern声明 */
CPU_DATA   OSPrioTbl[OS_PRIO_TBL_SIZE];

正如我们所说,优先级表是一个数组, 数组类型为CPU_DATA,在Cortex-M内核芯片的MCU中CPU_DATA为32位整型。

数组的大小由宏OS_PRIO_TBL_SIZE控制。
OS_PRIO_TBL_SIZE的具体取值与μC/OS-III支持多少个优先级有关,支持的优先级越多, 优先级表也就越大,需要的RAM空间也就越多。理论上μC/OS-III支持无限的优先级,只要RAM控制足够。

宏OS_PRIO_TBL_SIZE在os.h文件定义,具体实现见

#define  OS_PRIO_TBL_SIZE((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)
  • OS_CFG_PRIO_MAX表示支持多少个优先级, 在os_cfg.h中定义,本书设置为32,即最大支持32个优先级。
  • DEF_INT_CPU_NBR_BITS定义CPU整型数据有多少位, 本书适配的是基于Cortex-M系列的MCU,宏展开为32位。

所以,经过OS_CFG_PRIO_MAX和DEF_INT_CPU_NBR_BITS这两个宏展开运算之后,可得出OS_PRIO_TBL_SIZE的值为1, 即优先级表只需要一个成员即可表示32个优先级。如果要支持64个优先级,即需要两个成员,以此类推。 如果MCU的类型是16位、8位或者64位,只需要把优先级表的数据类型CPU_DATA改成相应的位数即可。

那么优先级表又是如何跟任务的优先级联系在一起的?具体的优先级表的示意图见图

在这里插入图片描述

2、 优先级表函数讲解

优先级表相关的函数在os_prio.c文件中实现,在os.h文件中声明,函数汇总具体见下表。

函数名称函数作用
OS_PrioInit初始化优先级表
OS_PrioInsert设置优先级表中相应的位
OS_PrioRemove清除优先级表中相应的位
OS_PrioGetHighest查找最高的优先级
1. OS_PrioInit()函数

OS_PrioInit()函数用于初始化优先级表,在OSInit()函数中被调用:

void OS_PrioInit( void )
{CPU_DATA i;/* 默认全部初始化为0 */for ( i=0u; i<OS_PRIO_TBL_SIZE; i++ ){OSPrioTbl[i] = (CPU_DATA)0;}
}

本章中,优先级表OS_PrioTbl[]只有一个成员,即OS_PRIO_TBL_SIZE等于1经过OS_PrioInit()初始化之后, 具体示意图见图:

在这里插入图片描述

2. OS_PrioInsert()函数

OS_PrioInsert()函数用于置位优先级表中相应的位,会被OSTaskCreate()函数调用

/* 置位优先级表中相应的位 */
void  OS_PrioInsert (OS_PRIO  prio)
{CPU_DATA  bit;CPU_DATA  bit_nbr;OS_PRIO   ix;/* 求模操作,获取优先级表数组的下标索引 */ix             = prio / DEF_INT_CPU_NBR_BITS;  //(1)/* 求余操作,将优先级限制在DEF_INT_CPU_NBR_BITS之内 */bit_nbr        = (CPU_DATA)prio & (DEF_INT_CPU_NBR_BITS - 1u);  //(2)/* 获取优先级在优先级表中对应的位的位置 */  //(3)bit            = 1u;bit          <<= (DEF_INT_CPU_NBR_BITS - 1u) - bit_nbr;/* 将优先级在优先级表中对应的位置1 */OSPrioTbl[ix] |= bit;  //(4)
}
  • (1):求模操作,获取优先级表数组的下标索引。即定位prio这个优先级对应优先级表数组的哪个成员。 假设prio等于3,DEF_INT_CPU_NBR_BITS(用于表示CPU一个整型数有多少位)等于32,那么ix就等于0,即对应OSPrioTBL[0]。
  • (2):求余操作,将优先级限制在DEF_INT_CPU_NBR_BITS之内, 超过DEF_INT_CPU_NBR_BITS的优先级就肯定要增加优先级表的数组成员了。假设prio等于3, DEF_INT_CPU_NBR_BITS(用于表示CPU一个整型数有多少位)等于32,那么bit_nbr就等于3, 但是这个还不是真正需要被置位的位。
  • (3):获取优先级在优先级表中对应的位的位置。置位优先级对应的位是从高位开始的, 不是从低位开始。位31对应的是优先级0,在μC/OS-III中,优先级数值越小,逻辑优先级就越高。 假设prio等于3,DEF_INT_CPU_NBR_BITS(用于表示CPU一个整型数有多少位)等于32,那么bit就等于28。
  • (4):将优先级在优先级表中对应的位置1。假设prio等于3, DEF_INT_CPU_NBR_BITS(用于表示CPU一个整型数有多少位)等于32,那么置位的就是OSPrioTbl[0]的位28。

在优先级最大是32,DEF_INT_CPU_NBR_BITS等于32的情况下,如果分别创建了优先级3、5、8和11这四个任务,任务创建成功后, 优先级表的设置情况是怎么样的?具体见下图
在这里插入图片描述

有一点要注意的是,在μC/OS-III中, 最高优先级和最低优先级是留给系统任务使用的,用户任务不能使用。

3. OS_PrioRemove()函数

OS_PrioRemove()函数用于清除优先级表中相应的位,与OS_PrioInsert()函数的作用刚好相反, 具体实现如下

/* 清除优先级表中相应的位 */
void  OS_PrioRemove (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;/* 将优先级在优先级表中对应的位清零 */OSPrioTbl[ix] &= ~bit;
}

与OS_PrioInsert()函数中不同的是置位操作改成了清零。

4. OS_PrioGetHighest()函数

OS_PrioGetHighest()函数用于从优先级表中查找最高的优先级

/* 获取最高的优先级 */
OS_PRIO  OS_PrioGetHighest (void)
{CPU_DATA  *p_tbl;OS_PRIO    prio;prio  = (OS_PRIO)0;/* 获取优先级表首地址 */p_tbl = &OSPrioTbl[0]; //		(1)/* 找到数值不为0的数组成员 */ 		//(2)while (*p_tbl == (CPU_DATA)0){prio += DEF_INT_CPU_NBR_BITS;p_tbl++;}/* 找到优先级表中置位的最高的优先级 */prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); 		//(3)return (prio);
}
  • (1):获取优先级表的首地址,从头开始搜索整个优先级表,直到找到最高的优先级。
  • (2):找到优先级表中数值不为0的数组成员,只要不为0就表示该成员里面至少有一个位是置位的。 我们知道,在下图图一 的优先级表中,优先级按照从左到右,从上到下依次减小,左上角为最高的优先级, 右下角为最低的优先级,所以我们只需要找到第一个不是0的优先级表成员即可。
  • (3):确定好优先级表中第一个不为0的成员后, 然后再找出该成员中第一个置1的位(从高位到低位开始找)就算找到最高优先级。
    在一个变量中, 按照从高位到低位的顺序查找第一个置1的位的方法是通过计算前导0函数CPU_CntLeadZeros()来实现的。
    从高位开始找1叫计算前导0,从低位开始找1叫计算后导0。
    如果分别创建了优先级3、5、8和11这四个任务, 任务创建成功后,优先级表的设置情况具体见下图图二。
    调用CPU_CntLeadZeros()可以计算出OSPrioTbl[0]第一个置1的位前面有3个0,那么这个3就是我们要查找的最高优先级, 至于后面还有多少个位置1我们都不用管,只需要找到第一个1即可。

在这里插入图片描述

在这里插入图片描述

CPU_CntLeadZeros()函数可由汇编或者C来实现,如果使用的处理器支持前导零指令CLZ,可由汇编来实现,加快指令运算,如果不支持则由C来实现。 在μC/OS-III中,这两种实现方法均有提供代码,到底使用哪种方法由CPU_CFG_LEAD_ZEROS_ASM_PRESEN这个宏来控制, 定义了这个宏则使用汇编来实现,没有定义则使用C来实现。

Cortex-M系列处理器自带CLZ指令,所以CPU_CntLeadZeros()函数默认由汇编编写,具体在cpu_a.asm文件实现, 在cpu.h文件声明

;*******************************************************************
;                            PUBLIC FUNCTIONS
;*******************************************************************EXPORT  CPU_CntLeadZerosEXPORT  CPU_CntTrailZeros;*******************************************************************
;                           计算前导0函数
;
; 描述:
;
; 函数声明: CPU_DATA  CPU_CntLeadZeros(CPU_DATA  val);
;
;*******************************************************************
CPU_CntLeadZerosCLZ     R0, R0                          ; Count leading zerosBX      LR;*******************************************************************
;                           计算后导0函数
;
; 描述:
;
; 函数声明: CPU_DATA  CPU_CntTrailZeros(CPU_DATA  val);
;
;*******************************************************************CPU_CntTrailZerosRBIT    R0, R0                          ; Reverse bitsCLZ     R0, R0                          ; Count trailing zerosBX      LR
/*
*******************************************************************
*                           函数声明
*                          cpu.h文件
*******************************************************************
*/
#define     CPU_CFG_LEAD_ZEROS_ASM_PRESEN
CPU_DATA    CPU_CntLeadZeros (CPU_DATA    val);    /* 在cpu_a.asm定义 */
CPU_DATA    CPU_CntTrailZeros(CPU_DATA  val);      /* 在cpu_a.asm定义 */

如果处理器不支持前导0指令,CPU_CntLeadZeros()函数就得由C编写,具体在cpu_core.c文件实现, 在cpu.h文件声明

#ifndef   CPU_CFG_LEAD_ZEROS_ASM_PRESENT
CPU_DATA  CPU_CntLeadZeros (CPU_DATA  val)
{CPU_DATA    nbr_lead_zeros;CPU_INT08U  ix;/* 检查高16位 */if (val > 0x0000FFFFu) {	//(1)/* 检查 bits [31:24] : */if (val > 0x00FFFFFFu) {	//(2)/* 获取bits [31:24]的值,并转换成8位 */ix             = (CPU_INT08U)(val >> 24u);	//(3)/* 查表找到优先级 */nbr_lead_zeros=(CPU_DATA)(CPU_CntLeadZerosTbl[ix]+0u);	//(4)}/* 检查 bits [23:16] : */else {/* 获取bits [23:16]的值,并转换成8位 */ix             = (CPU_INT08U)(val >> 16u);/* 查表找到优先级 */nbr_lead_zeros = (CPU_DATA  )(CPU_CntLeadZerosTbl[ix] +  8u);}}
/* 检查低16位 */
else {/* 检查 bits [15:08] : */if (val > 0x000000FFu) {/* 获取bits [15:08]的值,并转换成8位 */ix             = (CPU_INT08U)(val >>  8u);/* 查表找到优先级 */nbr_lead_zeros = (CPU_DATA  )(CPU_CntLeadZerosTbl[ix] + 16u);}/* 检查 bits [07:00] : */else {/* 获取bits [15:08]的值,并转换成8位 */ix             = (CPU_INT08U)(val >>  0u);/* 查表找到优先级 */nbr_lead_zeros = (CPU_DATA  )(CPU_CntLeadZerosTbl[ix] + 24u);}}/* 返回优先级 */return (nbr_lead_zeros);
}
#endif

在μC/OS-III中,由C实现的CPU_CntLeadZeros()函数支持8位、16位、32位和64位的变量的前导0计算, 但最终的代码实现都是分离成8位来计算。这里我们只讲解32位的,其他几种情况都类似。

  • (1):分离出高16位,else则为低16位。
  • (2):分离出高16位的高8位,else则为高16位的低8位。
  • (3):将高16位的高8位通过移位强制转化为8位的变量,用于后面的查表操作。
  • (4):将8位的变量ix作为数组CPU_CntLeadZerosTbl[]的索引, 返回索引对应的值,那么该值就是8位变量ix对应的前导0,然后再加上(24-右移的位数)就等于优先级。 数组CPU_CntLeadZerosTbl[]在cpu_core.c的开头定义
#ifndef   CPU_CFG_LEAD_ZEROS_ASM_PRESENT
static  const  CPU_INT08U  CPU_CntLeadZerosTbl[256] = {/*   索引           */8u,7u,6u,6u,5u,5u,5u,5u,4u,4u,4u,4u,4u,4u,4u,4u,  /*   0x00 to 0x0F   */3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,3u,  /*   0x10 to 0x1F   */2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,  /*   0x20 to 0x2F   */2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,2u,  /*   0x30 to 0x3F   */1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,  /*   0x40 to 0x4F   */1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,  /*   0x50 to 0x5F   */1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,  /*   0x60 to 0x6F   */1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,1u,  /*   0x70 to 0x7F   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0x80 to 0x8F   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0x90 to 0x9F   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0xA0 to 0xAF   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0xB0 to 0xBF   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0xC0 to 0xCF   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0xD0 to 0xDF   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,  /*   0xE0 to 0xEF   */0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u,0u   /*   0xF0 to 0xFF   */
};
#endif

对一个32位的变量算前导0个数的时候都是分离成8位的变量来计算,然后将这个8位的变量作为数组 CPU_CntLeadZerosTbl[]的索引,索引下对应的值就是这个8位变量的前导0个数。
一个8位的变量的取值范围为0~0XFF, 这些值作为数组CPU_CntLeadZerosTbl[]的索引,每一个值的前导0个数都预先算出来作为该数组索引下的值。

如 0x01的二进制为 0000 0001,所以前置零为 7
如 0x50的二进制为 0101 0000,所以前置零为 1

通过查CPU_CntLeadZerosTbl[]这个表就可以很快的知道一个8位变量的前导0个数,根本不用计算, 只是浪费了定义CPU_CntLeadZerosTbl[]这个表的一点点空间而已,在处理器内存很充足的情况下, 则优先选择这种空间换时间的方法。

3、就绪列表

准备好运行的任务的TCB都会被放到就绪列表中,系统可随时调度任务运行。
就绪列表在代码的层面上看就是一个 OS_RDY_LIST数据类型的数组OSRdyList[],数组的大小由宏OS_CFG_PRIO_MAX决定,支持多少个优先级, OSRdyList[]就有多少个成员。

任务的优先级与OSRdyList[]的索引一一对应,比如优先级3的任务的TCB会被放到OSRdyList[3]中。
OSRdyList[]是一个在os.h文件中定义的全局变量

/* 就绪列表定义 */
OS_EXT    OS_RDY_LIST    OSRdyList[OS_CFG_PRIO_MAX];

OS_RDY_LIST在os.h中定义,专用于就绪列表

typedefstruct  os_rdy_list         OS_RDY_LIST;		//(1)struct os_rdy_list {OS_TCB        *HeadPtr;		//(2)OS_TCB        *TailPtr;OS_OBJ_QTY    NbrEntries;		//(3)
};
  • (1):在μC/OS-III中,内核对象的数据类型都会用大写字母重新定义。
  • (2):OSRdyList[]的成员与任务的优先级一一对应, 同一个优先级的多个任务会以双向链表的形式存在OSRdyList[]同一个索引下,那么HeadPtr就用于指向链表的头节点, TailPtr用于指向链表的尾节点,该优先级下的索引成员的地址则称为该优先级下双向链表的根节点, 知道根节点的地址就可以查找到该链表下的每一个节点。
  • (3):NbrEntries表示OSRdyList[]同一个索引下有多少个任务。

一个空的就绪列表,OSRdyList[]索引下的HeadPtr、TailPtr和NbrEntrie都会被初始化为0,具体见图
在这里插入图片描述
就绪列表相关的所有函数都在os_core.c实现,这些函数都是以“OS_”开头,表示是OS的内部函数, 用户不能调用,这些函数的汇总具体见下表。

函数名称函数作用
OS_RdyListInit初始化就绪列表为空
OS_RdyListInsert插入一个TCB到就绪列表
OS_RdyListInsertHead插入一个TCB到就绪列表的头部
OS_RdyListInsertTail插入一个TCB到就绪列表的尾部
OS_RdyListMoveHeadToTail将TCB从就绪列表的头部移到尾部
OS_RdyListRemove将TCB从就绪列表中移除

TCB简介
操作系统中一个线程对应着一个TCB(Thread Control Block),叫做线程控制模块,控制着线程的运行和调度。
TCB组成
1、threadID:线程的唯一标识。
2、status:线程的运行状态
3、register:线程关于CPU中寄存器的情况
4、PC程序计数器:线程执行的下一条指令的地址
5、优先级:线程在操作系统调度的时候的优先级
6、线程的专属存储区:线程单独的存储区域
7、用户栈:线程执行的用户方法栈,用来保存线程当前执行的用户方法的信息
8、内核栈:线程执行的内核方法栈,用来保存线程当前执行的内核方法信息。

4、就绪列表函数讲解

在实现就绪列表相关函数之前,我们需要在结构体os_tcb中添加Prio、NextPtr和PrevPtr这三个成员, 然后在os.h中定义两个全局变量OSPrioCur和OSPrioHighRdy。
接下来要实现的就绪列表相关的函数会用到几个变量。

struct os_tcb {CPU_STK         *StkPtr;CPU_STK_SIZE    StkSize;/* 任务延时周期个数 */OS_TICK         TaskDelayTicks;/* 任务优先级 */OS_PRIO         Prio;/* 就绪列表双向链表的下一个指针 */OS_TCB          *NextPtr;/* 就绪列表双向链表的前一个指针 */OS_TCB          *PrevPtr;
};/* 在os.h中定义 */
OS_EXT    OS_PRIO  OSPrioCur;       /* 当前优先级 */
OS_EXT    OS_PRIO  OSPrioHighRdy;   /* 最高优先级 */

1. OS_RdyListInit()函数

OS_RdyListInit()用于将就绪列表OSRdyList[]初始化为空
在这里插入图片描述

void OS_RdyListInit(void)
{OS_PRIO i;OS_RDY_LIST *p_rdy_list;/* 循环初始化,所有成员都初始化为0 */for ( i=0u; i<OS_CFG_PRIO_MAX; i++ ) {p_rdy_list = &OSRdyList[i];p_rdy_list->NbrEntries = (OS_OBJ_QTY)0;p_rdy_list->HeadPtr = (OS_TCB *)0;p_rdy_list->TailPtr = (OS_TCB *)0;}
}

2. OS_RdyListInsertHead()函数

OS_RdyListInsertHead()用于在链表头部插入一个TCB节点,插入的时候分两种情况,第一种是链表是空链表, 第二种是链表中已有节点,具体示意图见图
在这里插入图片描述

void  OS_RdyListInsertHead (OS_TCB  *p_tcb)
{OS_RDY_LIST  *p_rdy_list;OS_TCB       *p_tcb2;/* 获取链表根部 */p_rdy_list = &OSRdyList[p_tcb->Prio];/* CASE 0: 链表是空链表 */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;}/* CASE 1: 链表已有节点 */else {p_rdy_list->NbrEntries++;p_tcb->NextPtr         = p_rdy_list->HeadPtr;p_tcb->PrevPtr         = (OS_TCB    *)0;p_tcb2                 = p_rdy_list->HeadPtr;p_tcb2->PrevPtr        = p_tcb;p_rdy_list->HeadPtr    = p_tcb;}
}

3. OS_RdyListInsertTail()函数

OS_RdyListInsertTail()用于在链表尾部插入一个TCB节点,插入的时候分两种情况,第一种是链表是空链表, 第二种是链表中已有节点
在这里插入图片描述

void  OS_RdyListInsertTail (OS_TCB  *p_tcb)
{OS_RDY_LIST  *p_rdy_list;OS_TCB       *p_tcb2;/* 获取链表根部 */p_rdy_list = &OSRdyList[p_tcb->Prio];/* CASE 0: 链表是空链表 */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;}/* CASE 1: 链表已有节点 */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;}
}

4. OS_RdyListInsert()函数

OS_RdyListInsert()用于将任务的TCB插入就绪列表,插入的时候分成两步。
第一步是根据优先级将优先级表中的相应位置位, 这个调用OS_PrioInsert()函数来实现。
第二步是根据优先级将任务的TCB放到OSRdyList[优先级]中, 如果优先级等于当前的优先级则插入链表的尾部,否则插入链表的头部

/* 在就绪链表中插入一个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);}
}

5. OS_RdyListMoveHeadToTail()函数

OS_RdyListMoveHeadToTail()函数用于将节点从链表头部移动到尾部,移动的时候分四种情况。
第一种是链表为空,无事可做;
第二种是链表只有一个节点,也是无事可做;
第三种是链表只有两个节点;
第四种是链表有两个以上节点
在这里插入图片描述

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. OS_RdyListRemove()函数

OS_RdyListRemove()函数用于从链表中移除一个节点,移除的时候分为三种情况,第一种是链表为空,无事可做; 第二种是链表只有一个节点;第三种是链表有两个以上节点,具体示意图见图
在这里插入图片描述

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_PrioRemove(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的PrevPtr和NextPtr这两个指针 */p_tcb->PrevPtr = (OS_TCB *)0;p_tcb->NextPtr = (OS_TCB *)0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/742194.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络 —— 运输层

运输层 5.1 运输层概述 运输层的主要任务是&#xff0c;如何为运行在不同主机上的应用进程提供直接的通信服务。运输层协议又称为端到端协议。 根据应用需求的不同&#xff0c;因特网的运输层为应用层提供了两种不同的运输协议&#xff0c;即面向连接的TCP和无连接的UDP 5.2…

金融需要多样性,量化需要C++!通过本文,你可以知道:1、为什么是C++

通过本文&#xff0c;你可以知道&#xff1a; 1、为什么是C 2、Python的用武之地 3、量化C岗位薪酬水平 C VS Python 量化交易系统开发语言主要用C&#xff0c;也有人用Python。 但是从经验看&#xff0c;用C开发的量化交易系统能够让在系统中程序运行的速度更快。 量化交易…

Maya为模型添加细分数

文章目录 方法1 通道盒操作注意 方法2 添加分段操作注意 方法3 平滑操作 方法1 通道盒 操作 选中物体&#xff0c;选择通道盒/输入/点一下模型的名字/细分数&#xff0c;分别为长宽高添加细分 这相当于修改模型的底层数据&#xff0c;不会平滑模型&#xff0c;只会进行细分 …

瑞熙贝通实验室物联网管理平台新升级|支持远程开门视频监控与电源控制以及环境监测

瑞熙贝通实验室智能物联网管控平台&#xff1a;利用“互联网与物联网技术”有机融合&#xff0c;对实验室的用电安全监测、实验室环境异常监测&#xff08;颗粒物监测、明火监测、可燃气体、烟雾监测、温湿度传感器、红外人体感应&#xff09;、实验室人员安全准入、万物互联等…

爬虫案例1

通过get请求直接获取电影信息 目标页面: https://spa6.scrape.center/在network中可以看到是通过Ajax发送的请求&#xff0c;这个请求在postman中也可以直接请求成功&#xff0c;这只是一个用来练习爬虫的&#xff0c;没有达到js逆向的过程&#xff0c;需要通过分析js 代码来获…

Excel判断CD两列在EF两列的列表中是否存在

需求 需要将CD两列的ID和NAME组合起来&#xff0c;查询EF两列的ID和NAME组合起来的列表中是否存在&#xff1f; 比如&#xff0c;判断第二行的“123456ABC”在EF的第二行到第四行中是否存在&#xff0c;若存在则显示Y&#xff0c;不存在则显示N 实现的计算公式 IF(ISNUMBER…

淘宝基于Nginx二次开发的Tengine服务器

最近在群里看到这样一张阿里云网关报错的截图&#xff0c;我保存下来看了下 看到下面有 Tengine提供技术支持&#xff0c;这个Tengine是什么东西呢&#xff1f;我搜索了下似乎是淘宝在nginx的基础上自己改的Web服务器 Tengine还支持OpenResty框架&#xff0c;该框架是基于Ngin…

SAR ADC学习笔记(5)

高精度比较器 一、基于开环运放的比较器 OP开环应用时不需要考虑频率特性(相位裕度那些) &#xff0c;不存在稳定性问题。 单级运放的时域响应 多级运放 二、Latch比较器 Latch比较器的速度 Latch比较器的噪声优化 三、高速高精度比较器 消除失调电压OFFSET OOS IOS

nRF52832——唯一 ID 与加密解密

nRF52832——唯一 ID 与加密解密 唯一 ID 概念唯一 ID 作用读取唯一 ID 唯一 ID 用于加密TEA 加密算法唯一 ID 的加密和解密 唯一 ID 概念 唯一 ID 作用 nRF52xx 微控制器提供一组 64 位的唯一 ID 号&#xff0c;这个唯一身份标识所提供的 ID 值对任意一个 nRF52xx 微控制器&…

展会邀约 | 加速科技将携重磅产品亮相SEMICON China 2024

SEMICON China 2024将于3月20日-3月22日在上海新国际博览中心隆重举行。展会期间&#xff0c;加速科技将携重磅产品高性能数模混合信号测试机ST2500EX、LCD Driver测试机Flex10K-L、高密度数模混合信号测试系统ST2500E、高性能数模混合信号测试系统ST2500A亮相此次行业盛会&…

WEB区块链开发组件 - KLineChart

当我们开发区块链的时候&#xff0c;实现K线可能大家会想到EChart&#xff0c;但是EChart做可能需要耗费大量工作量&#xff0c;实现出来的功能估计也是牵强着用。 这时候&#xff0c;我们可能网上会搜索到TradingView,可是这个组件虽然功能非常强大&#xff0c;但是还是要费事…

Android7.1 ANR error 弹窗处理

Android7.1 ANR error 弹窗处理 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip OS:Android 7.1.2 Kernel: 3.10 问题描述 有时会用到第三方apk&#xff0c;内置到系统中&…

全国车辆识别代码信息API查询接口-VIN深度解析

我们先来介绍下什么是vin码&#xff0c;以及vin码的构成结构解析&#xff0c;汽车VIN码&#xff0c;也叫车辆识别号码&#xff0c;通俗可以理解为汽车的身份证号码。 VIN码一共分四大部分&#xff1a; 1~3位&#xff0c;是世界制造厂识别代号&#xff08;WMI&#xff09;&…

DevOps本地搭建笔记(个人开发适用)

需求和背景 win11 wsl2 armbian(玩客云矿渣&#xff09;&#xff0c;构建个人cicd流水线&#xff0c;提高迭代效率。 具体步骤 基础设施准备 硬件准备&#xff1a;一台笔记本&#xff0c;用于开发和构建部署&#xff0c;一台服务器&#xff0c;用于日常服务运行。 笔记本…

如何批量注册无数个电子邮箱

需求 最近我们需要批量注册无数个steam账号来做一些不可描述的事情&#xff0c;那么我们就需要先批量注册无数个电子邮箱Email 而且我们希望注册和接收邮件是可以通过脚本自动化的&#xff0c;最好是通过正常的API接口 什么叫正常的API接口&#xff1f;正常的API接口是指邮件…

Antd中s-table组件某字段进行排序

Antd中s-table组件某字段进行排序 提前说明&#xff0c;s-table组件包含分页等功能 <s-tableref"table":columns"columns":data"loadData"bordered:row-key"(record) > record.id"></s-table>而其中loadData为获取表数…

线性表的顺序存储实验

附 录 #include<stdio.h> #include<stdlib.h>typedef struct {double* data;int length; }Sqlist;void initSqlist(Sqlist* L, int n) {L->data (double*)malloc(n * sizeof(double)); if (L->data NULL){printf("内存分配失败\n");exit(1);}pri…

MTK安卓开发板_联发科开发板评估套件_安卓主板硬件开发

在介绍开发板之前&#xff0c;让我们先来区分一下核心板和开发板的区别。核心板是一种集成度高、功能完整的计算模块&#xff0c;搭载系统&#xff0c;简化了外围接口&#xff0c;体积尺寸相对较小&#xff0c;主要适用于嵌入式系统。而开发板由核心板底板组成&#xff0c;提供…

Python实战:采集全国5A景点名单

本文将以采集全国 5A 景点名单为例&#xff0c;详细介绍如何使用 Python 进行数据采集。 本文采集到全国340家5A景区的名单&#xff0c;包括景区名称、地区、 A级、评定年份这些字段。 一、分析数据源 为了获取权威数据&#xff0c;我们来到主管部门的官方网站&#xff0c;在右…

Java集合中经典的 5种设计模式,打死也要记住啊!

集合 一、 迭代器模式(Iterator Pattern)二、 工厂模式(Factory Pattern)三、 装饰器模式(Decorator Pattern)四、 适配器模式(Adapter Pattern)五、 组合模式(Composite Pattern) Java 集合框架中的 List、Set、Map 以及其实现类都使用了多种经典的设计模式 一、 迭代器模式(I…