(学习日记)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_CntLeadZeros
        EXPORT  CPU_CntTrailZeros

;*******************************************************************
;                           计算前导0函数
;
; 描述:
;
; 函数声明: CPU_DATA  CPU_CntLeadZeros(CPU_DATA  val);
;
;*******************************************************************
CPU_CntLeadZeros
        CLZ     R0, R0                          ; Count leading zeros
        BX      LR



;*******************************************************************
;                           计算后导0函数
;
; 描述:
;
; 函数声明: CPU_DATA  CPU_CntTrailZeros(CPU_DATA  val);
;
;*******************************************************************

CPU_CntTrailZeros
        RBIT    R0, R0                          ; Reverse bits
        CLZ     R0, R0                          ; Count trailing zeros
        BX      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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mqtt mosquitto 资料

MQTT 3.1.1 协议中文版 | MQTT中文网mqtt中文网http://mqtt.p2hp.com/mqtt311MQTT Version 3.1.1http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718009 mqttclient: 一个基于socket API之上的跨平台MQTT客户端&#xff0c;拥有非常简洁的API接口…

CVE-2023-38836 BoidCMSv.2.0.0 后台文件上传漏洞

漏洞简介 BoidCMS是一个免费的开源平面文件 CMS&#xff0c;用于构建简单的网站和博客&#xff0c;使用 PHP 开发并使用 JSON 作为数据库。它的安装无需配置或安装任何关系数据库&#xff08;如 MySQL&#xff09;。您只需要一个支持PHP 的Web服务器。在 BoidCMS v.2.0.0 中存…

水库泄洪监测预警系统解决方案

一、方案概述 近年来由于危险河道管理措施不到位&#xff0c;调峰水库泄水风险长期存在&#xff0c;信息通报制度缺失以及民众安全警觉性不高等因素导致的水库泄洪时冲走下游河道游客以及人民财产的事故频发。水库安全度汛是全国各地防汛抗洪的重中之重&#xff0c;而水库泄洪监…

【PyTorch][chapter 22][李宏毅深度学习]【无监督学习][ WGAN]【理论二】

前言&#xff1a; 本篇主要参考《Wasserstein GAN and the Kantorovich-Rubinstein Duality》 重点介绍一下 WGAN 的损失函数 是如何通过 Wasserstein Distance 变换过来的。 分为5步&#xff1a; 我们首先建立Wasserstein Distance 极小值形式&#xff0c; 经过对…

Linly-Talker智能数字人实时对话系统如何部署体验

环境&#xff1a; Linly-Talker 问题描述&#xff1a; Linly-Talker智能数字人实时对话系统如何部署体验 Linly-Talker 是一个智能 AI 系统&#xff0c;它将大型语言模型 &#xff08;LLMs&#xff09; 与视觉模型相结合&#xff0c;创造出一种新颖的人机交互方式。它集成了…

【HTML】1px边框与1px分割线

对比图 箭头标注的是处理过的 1px分割线 使用transform的scaleY进行缩小 码 <div class"mini-heriz"></div><br><div style"border: solid 1px black; width: 300px;height: 1px;"></div> <style> .mini-heriz {wi…

Linux编程4.2 网络编程-协议

1、为什么要有协议&#xff1f; 计算机网络中实现通信必须有一些约定&#xff0c;如对速率、传输代码、代码结构、传输控制步骤和出错控制等约定&#xff0c;这些约定即被称为通信协议。在两个节点之间要成功地进得通信&#xff0c;两个节点之间必须约定使用共同的“语言”&am…

MySQL用法---MySQL Workbench创建数据库和表

1. 连接数据库 打开软件&#xff0c;点击左下角卡片&#xff0c;输入设置的数据库密码&#xff0c;勾选单选框 2. 了解主页面的组成部分 3. 创建数据库 先点击工具栏的创建按钮 再输入数据库名称 点击 Apply 创建 4. 创建数据表 展开数据库&#xff0c;在Tables上右键&…

如何在WordPress网站上设置多语言展示

在今天的全球化世界中&#xff0c;拥有多语言网站对于吸引更广泛的受众至关重要。前不就我们遇到Hostease的客户咨询我们的在线客服&#xff0c;他想要对他的wordpress网站支持多语言。我们提供给客户可以尝试以下的插件来支持多语言。 在本教程中&#xff0c;我们将逐步介绍如…

cpp qt 一个奇怪的bug

今天在用cpp qt的时候发现了一个奇怪的东西 这是我的源代码 #include "mywidget.h" #include <QPushButton>myWidget::myWidget(QWidget *parent): QWidget(parent) {QPushButton * btn1 new QPushButton;btn1->show();btn1->setParent(this);btn1-&g…

冥想与AI:打造定制的放松体验

如今&#xff0c;在浏览网页或社交网络时&#xff0c;您似乎很难对一条条心理健康信息无动于衷。遇到这种情况的可不只是您。当今不断变化的时代给人们平添压力&#xff0c;企业纷纷利用智能技术满足人们的减压需求&#xff0c;让人们的生活多一些平和从容。 冥想就是一种练习呼…

低代码开发能否降低程序员门槛?技能需求分析与优势评估揭秘!

一&#xff0e;什么是低代码开发平台 低代码开发平台和零代码开发平台是近几年时兴的一种新的程序开发方法。该模式的特征是可以使用用户界面、拖拽操作等方式快速构建应用软件软件&#xff0c;从而减少开发者的学习标准&#xff0c;使每个人都能变成开发者。 但它仍然是基于…

别再写传统简历了!AI简历5个超实用的功能,助你求职一臂之力(强烈建议收藏)

你们在制作简历时,是不是基本只关注两件事:简历模板,还有基本信息的填写。 当你再次坐下来更新你的简历时,可能会发现自己不自觉地选择了那个“看起来最好看的模板”,填写基本信息,却没有深入思考如何使简历更具吸引力。这其实是一个普遍现象:许多求职者仍停留在传统简历…

在使用qml的qmldir文件创建常用组件报错unknow component

解决方法&#xff1a;Qt Creator中的工具-->QML/JS-->重置代码模型 参考博文:QML自定义模块及qmldir的使用_同一资源文件目录下的qml模块使用-CSDN博客 不一样的地方是我给我的文件起了别名 以及我的qrc文件路径有前缀/qml 总体操作&#xff1a; 1.使用模块中的组件时…

ChatGPT提问技巧:可解释的软提示

ChatGPT提问技巧&#xff1a;可解释的软提示 可解释的软提示是一种既能控制模型生成的文本&#xff0c;又能为模型提供一定灵活性的技术。 具体做法是为模型提供一组受控输入和一些有关所需输出的附加信息。这种技术可以使生成的文本更具可解释性和可控性。 提示示例及其公式…

Mysql 学习(十七)事务隔离级别和MVCC

前提准备 首先创建一个表&#xff1a; CREATE TABLE hero (number INT,name VARCHAR(100),country varchar(100),PRIMARY KEY (number) ) EngineInnoDB CHARSETutf8;INSERT INTO hero VALUES(1, 刘备, 蜀);事务隔离级别 mysql 是一个 客户端 和 服务器架构的软件&#xff0c…

【Linux操作系统】:Linux进程概念(2)

一、Z(zombie)-僵尸进程 1.僵尸进程概念 故事 张三每天都有跑步的习惯&#xff0c;这一天他和往常一样跑步&#xff0c;跑了两三圈&#xff0c;突然跑在它前面的一个人倒在地上不动了&#xff0c;作为热心市民张三赶紧报警并且拨打120。很快120就来了&#xff0c;但是没过几分…

C#、C++、Java、Python 选择哪个好?

作者&#xff1a;网博汇智 链接&#xff1a;https://www.zhihu.com/question/298323023/answer/2789627224 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 一个好的程序员不能把自己绑定在一种语言上&#xff0c;不…

摄像机内存卡删除的视频如何恢复?恢复指南来袭

在现代社会&#xff0c;摄像机已成为记录生活、工作和学习的重要设备。然而&#xff0c;随着使用频率的增加&#xff0c;误删或意外丢失视频的情况也时有发生。面对这样的情况&#xff0c;许多用户可能会感到无助和困惑。那么&#xff0c;摄像机内存卡删除的视频真的无法恢复吗…

X小蓝鸟,已破!支持批量下载且速度飞快

从马斯克把小蓝鸟改名之后&#xff0c;最大的感觉就是域名更好记了&#xff0c;对于软件爱好者来说&#xff0c;可以关注软件最新消息&#xff0c;许多软件开发者都是在上面更新动态。 本期给大家分享一款可以下载X上的图片/视频工具X-Spider&#xff0c;它的特点是开源免费&am…