μC/OSIII学习day3
介绍
临界段,就绪列表
# 临界段
# 临界段简介
临界段用一句话概括就是一段在执行的时候不能被中断的代码段.
系统调度和外部中断会打断临界段.
μC/OS对临界段的保护最终还是对中断的开和关的控制.
μC/OS中定义了一个进入临界段的宏
OS_CRITICAL_ENTER()
和两个出临界段的宏OS_CRITICAL_EXIT()
,OS_CRITICAL_EXIT_NO_SCHED()
.此外还有一个开中断但是锁定调度器的宏定义OS_CRITICAL_ENTER_CPU_EXIT()
.
# Cortex-M内核快速关中断指令
为了快速的开关中断,Cortex-M内核专门设置了一条GPS指令,有四种用法.
CPSID I;PRIMASK=1;//关中断 CPSIE I;PRIMASK=0;//开中断 CPSID F;FAULTMASK=1;//关异常 CPSIE F;FAULTMASK=0;//开异常
1
2
3
4PRIMASK
和FAULTMASK
是Cortex-M内核里面三个中断屏蔽寄存器中的两个,还有一个是BASEPRI
名称 功能描述 PRIMASK 这是个只有单一比特的寄存器.在它被置1后,就关掉所有可屏蔽的异常,只剩下NMI和硬FAULT可以响应.它的默认值是0,表示没有关中断. FAULTMASK 这是个只有1个位的寄存器.当它置1时,只有NMI才能响应,所有其他的异常,甚至是硬FAULT,也通通闭嘴.它的默认值也是0,表示没有关异常. BASEPRI 这个寄存器最多有9位(由表达优先级的位数决定).它定义了被屏蔽优先级的阈值.当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低).但若被设成0,则不关闭任何中断,0 也是默认值. 在μC/OS中通常用CPSID I指令来立即关闭中断.
# 关中断
μC/OS的关中断函数
CPU_SR_Save()
(cpu_a.asm).CPU_SR_Save MRSR0, PRIMASK CPSID I BX LR
1
2
3
4
5
# 开中断
μC/OS的开中断函数
CPU_SR_Restore()
(cpu_a.asm).CPU_SR_Restore MSR PRIMASK, R0 BX LR
1
2
3
4
# 临界段代码的应用
我们发现开启关闭中断并没有简单的调用CPS指令,而是添加了一些代码,在进入临界段的时候保存PRIMASK的值,防止开关中断嵌套时发生混乱.
错误应用
PRIMASK = 0;/* PRIMASK 初始值为 0,表示没有关中断 */ /* 临界段代码 */ { /* 临界段 1 开始 */ CPU_SR_Save();/* 关中断,PRIMASK = 1 */ { /* 临界段 2 */ CPU_SR_Save();/* 关中断,PRIMASK = 1 */ { } CPU_SR_Restore();/* 开中断,PRIMASK = 0 */(注意) } /* 临界段 1 结束 */ CPU_SR_Restore();/* 开中断,PRIMASK = 0 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16容易看出,临界段未结束时中断已经开启.
正确应用
PRIMASK = 0;/* PRIMASK 初始值为 0,表示没有关中断 */ CPU_SR cpu_sr1 = (CPU_SR)0 CPU_SR cpu_sr2 = (CPU_SR)0 /* 临界段代码 */ { /* 临界段 1 开始 */ cpu_sr1 = CPU_SR_Save();/* 关中断,cpu_sr1=0,PRIMASK=1 */ { /* 临界段 2 */ cpu_sr2 = CPU_SR_Save();/* 关中断,cpu_sr2=1,PRIMASK=1 */ { } CPU_SR_Restore(cpu_sr2);/* 开中断,cpu_sr2=1,PRIMASK=1 */ } /* 临界段 1 结束 */ CPU_SR_Restore(cpu_sr1);/* 开中断,cpu_sr1=0,PRIMASK=0 */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21区别在于,
cpu_sr1
和cpu_sr2
作为函数的形参传入开中断函数.
# 测量开关中断时间
- 系统会在每次关中断前开始测量,开中断后结束测量,测量功能保存了2个方面的测量值,总的关中断时间与最近一次关中断的时间.
- 过设置cpu_cfg.h文件中的宏定义
CPU_CFG_INT_DIS_MEAS_EN
为1就表示启用该功能.
测量中断时间初始化
关中断之前要用
CPU_IntDisMeasInit()
函数进行初始化#ifdef CPU_CFG_INT_DIS_MEAS_EN static void CPU_IntDisMeasInit(void){ CPU_TS_TMR time_meas_tot_cnts; CPU_INT16U i; CPU_SR_ALLOC(); CPU_IntDisMeasCtr = 0u; CPU_IntDisNestCtr = 0u; CPU_IntDisMeasStart_cnts = 0u; CPU_IntDisMeasStop_cnts = 0u; CPU_IntDisMeasMaxCur_cnts = 0u; CPU_IntDisMeasMax_cnts = 0u; CPU_IntDisMeasOvrhd_cnts = 0u; time_meas_tot_cnts = 0u; CPU_INT_DIS(); /* 关中断 */ for (i = 0u; i < CPU_CFG_INT_DIS_MEAS_OVRHD_NBR; i++) { CPU_IntDisMeasMaxCur_cnts = 0u; CPU_IntDisMeasStart();/* 执行多个连续的开始/停止时间测量 */ CPU_IntDisMeasStop(); time_meas_tot_cnts += CPU_IntDisMeasMaxCur_cnts; /* 计算总的时间 */ } //测量结果保存在CPU_IntDisMeasOvrhd_cnts CPU_IntDisMeasOvrhd_cnts = (time_meas_tot_cnts +(CPU_CFG_INT_DIS_MEAS_OVRHD_NBR/2u))/CPU_CFG_INT_DIS_MEAS_OVRHD_NBR; /* 得到平均值,就是每一次测量额外消耗的时间 */ CPU_IntDisMeasMaxCur_cnts = 0u; CPU_IntDisMeasMax_cnts = 0u; CPU_INT_EN(); } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31测量最大关中断时间
启用了
CPU_CFG_INT_DIS_MEAS_EN
这个宏定义,那么系统在关中断的时候会调用了开始测量关中断最大时间的函数CPU_IntDisMeasStart()
,开中断的时候调用停止测量关中断最大时间的函数CPU_IntDisMeasStop()
./* 开始测量关中断时间 */ #ifdef CPU_CFG_INT_DIS_MEAS_EN void CPU_IntDisMeasStart(void){ CPU_IntDisMeasCtr++; if (CPU_IntDisNestCtr == 0u) /* 嵌套层数为 0 */ { CPU_IntDisMeasStart_cnts = CPU_TS_TmrRd(); /* 保存时间戳 */ } CPU_IntDisNestCtr++; } #endif /* 停止测量关中断时间 */ #ifdef CPU_CFG_INT_DIS_MEAS_EN void CPU_IntDisMeasStop(void){ CPU_TS_TMR time_ints_disd_cnts; CPU_IntDisNestCtr--; if(CPU_IntDisNestCtr == 0u) /* 嵌套层数为 0*/ { CPU_IntDisMeasStop_cnts = CPU_TS_TmrRd(); /* 保存时间戳 */ time_ints_disd_cnts = CPU_IntDisMeasStop_cnts - CPU_IntDisMeasStart_cnts;/* 得到关中断时间 */ /* 更新最大关中断时间 */ if(CPU_IntDisMeasMaxCur_cnts < time_ints_disd_cnts) { CPU_IntDisMeasMaxCur_cnts = time_ints_disd_cnts; } if(CPU_IntDisMeasMax_cnts < time_ints_disd_cnts) { CPU_IntDisMeasMax_cnts = time_ints_disd_cnts; } } } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36在关中断且嵌套层数
OSSchedLockNestingCtr
为0的时候保存下时间戳,如果嵌套层数不为0,肯定不是刚刚进入中断,退出中断且嵌套层数为0的时候,这个时候才算是真正的退出中断,把测得的时间戳减去一次测量额外消耗的时间,便得到这次关中断的时间,再将这个时间跟历史保存下的最大的关中断的时间对比.获取最大关中断时间
μC/OS提供了三个与获取关中断时间有关的函数,分别是:
CPU_IntDisMeasMaxCurGet()
获取整个程序运行过程中最大的关中断时间.
#ifdef CPU_CFG_INT_DIS_MEAS_EN//如果启用了关中断时间测量 //获取测量的程序段的最大关中断时间 CPU_TS_TMR CPU_IntDisMeasMaxCurGet(void){ CPU_TS_TMR time_tot_cnts; CPU_TS_TMR time_max_cnts; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存 SR),开中断时将该值还原. CPU_INT_DIS(); //关中断 time_tot_cnts = CPU_IntDisMeasMaxCur_cnts; //获取未处理的程序段最大关中断时间 CPU_INT_EN(); //开中断 time_max_cnts = CPU_IntDisMeasMaxCalc(time_tot_cnts); //获取减去测量时间后的最大关中断时间 return (time_max_cnts); //返回程序段的最大关中断时间 } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19CPU_IntDisMeasMaxCurReset()
和CPU_IntDisMeasMaxGet()
在程序前调用
CPU_IntDisMeasMaxCurReset()
函数将CPU_IntDisMeasMaxCur_cnts
变量清0,在程序结束的时候调用CPU_IntDisMeasMaxCurGet()
函数即可.#ifdef CPU_CFG_INT_DIS_MEAS_EN//如果启用了关中断时间测量 //初始化(复位)测量程序段的最大关中断时间 CPU_TS_TMR CPU_IntDisMeasMaxCurReset(void) { CPU_TS_TMR time_max_cnts; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存 SR),开中断时将该值还原. time_max_cnts=CPU_IntDisMeasMaxCurGet();//获取复位前的程序段最大关中断时间 CPU_INT_DIS(); //关中断 CPU_IntDisMeasMaxCur_cnts = 0u; //清零程序段的最大关中断时间 CPU_INT_EN(); //开中断 return (time_max_cnts); //返回复位前的程序段最大关中断时间 } #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN//如果启用了关中断时间测量 //获取整个程序目前最大的关中断时间 CPU_TS_TMR CPU_IntDisMeasMaxGet(void){ CPU_TS_TMR time_tot_cnts; CPU_TS_TMR time_max_cnts; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 //SR(临界段关中断只需保存SR),开中断时将该值还原 CPU_INT_DIS(); //关中断 time_tot_cnts = CPU_IntDisMeasMax_cnts; //获取尚未处理的最大关中断时间 CPU_INT_EN(); //开中断 time_max_cnts = CPU_IntDisMeasMaxCalc(time_tot_cnts); //获取减去测量时间后的最大关中断时间 return (time_max_cnts); //返回目前最大关中断时间 } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 就绪列表
- 在μC/OS中使用
OSTaskCreate()
函数创建任务后,任务的TCB会被放入就绪列表中. - 就序列表包含一个表示任务优先级的优先表,一个储存任务TCB的TCB双向链表.
# 优先级表
优先级表是在os_prio.c文件开头定义的一个数组.
CPU_DATA OSPrioTbl[OS_PRIO_TBL_SIZE]; #define OS_PRIO_TBL_SIZE ((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)
1
2
3
4OS_CFG_PRIO_MAX
(os_cfg.h)表示支持多少个优先级.DEF_INT_CPU_NBR_BITS
定义CPU整型数据有多少位,Cortex-M为32位.默认情况下
OS_CFG_PRIO_MAX
为32,经计算OS_PRIO_TBL_SIZE
的值为1,由此可知优先级表只需要一个成员就可以表示32个优先级.优先级表示意图
创建一个优先级为Prio的任务,那么就在
OSPrioTbl[0]
的位[31-prio]置1即可.判断单片机是低位优先还是高位优先
以Byte为最小单位
/* int i=10; 内存从低到高(低位优先) 00001010 00000000 00000000 00000000 内存从高到低(高位优先) 00000000 00000000 00000000 00001010 */ void Sequence_validation(void){ int a=10; short b; memcpy(&b,&a,2); printf("%d\n",b); } /* 如果输出结果为10,储存是低位优先 如果输出结果为0,储存是高位优先 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17实测:
以bit为最小单位
数据都是左高右低.
# 优先级表函数
函数名称 | 函数作用 |
---|---|
OS_PrioInit() | 初始化优先级表 |
OS_PrioInsert() | 设置优先级表中相应的位 |
OS_PrioRemove() | 清除优先级表中相应的位 |
OS_PrioGetHighest() | 查找最高的优先级 |
OS_PrioInit()
函数/* 初始化优先级表 */ void OS_PrioInit( void ){ CPU_DATA i; /* 默认全部初始化为 0 */ for ( i=0u; i<OS_PRIO_TBL_SIZE; i++ ) { OSPrioTbl[i] = (CPU_DATA)0; } }
1
2
3
4
5
6
7
8
9
10OS_PrioInsert()
函数/* 置位优先级表中相应的位 */ void OS_PrioInsert(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; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19OS_PrioRemove()
函数/* 清除优先级表中相应的位 */ 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; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19OS_PrioGetHighest()
函数/* 获取最高的优先级 */ 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); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19CPU_CntLeadZeros
函数是由高位向低位查找第一个1前面有几个0.OS_PrioGetHighest()
函数的大致原理:prio=优先级表数(纵向)*32+第一个1前面有几个0(横向).
# 就绪列表(函数级)
准备好运行的任务的TCB都会被放到就绪列表中,系统可随时调度任务运行.
就序列表是在os.h文件开头定义的一个数组.
OS_EXT OS_RDY_LIST OSRdyList[OS_CFG_PRIO_MAX]; typedef struct os_rdy_list OS_RDY_LIST; struct os_rdy_list { OS_TCB *HeadPtr; OS_TCB *TailPtr; #if (OS_CFG_DBG_EN == DEF_ENABLED) OS_OBJ_QTY NbrEntries; #endif };
1
2
3
4
5
6
7
8
9
10
11OSRdyList[]
的成员数与任务的最大优先级相同,同一个优先级的多个任务会以双向链表的形式存在于OSRdyList[]
的同一个索引下,HeadPtr
用于指向链表的头节点,TailPtr
用于指向链表的尾节点,该优先级下的索引成员的地址称为该优先级下双向链表的根节点,知道根节点的地址就可以查到该链表下的每一个节点.NbrEntries
表示OSRdyList[]
同一个索引下有多少个任务.初始化空的就绪列表时,
OSRdyList[]
索引下的成员都为0.
# 就序列表相关函数
就绪列表相关的所有函数都在 os_core.c 实现,这些函数都是以OS_
开头,表示是OS的内部函数,用户不能调用.
函数名称 | 函数作用 |
---|---|
OS_RdyListInit() | 初始化就绪列表为空 |
OS_RdyListInsert() | 插入一个TCB到就绪列表 |
OS_RdyListInsertHead () | 插入一个TCB到就绪列表的头部 |
OS_RdyListInsertTail() | 插入一个TCB到就绪列表的尾部 |
OS_RdyListMoveHeadToTail() | 将TCB从就绪列表的头部移到尾部 |
OS_RdyListRemove () | 将TCB从就绪列表中移除 |
//相关结构体和变量
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; /* 最高优先级 */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13OS_RdyListInsert()
函数//将任务的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); } }
1
2
3
4
5
6
7
8
9
10
11
12
13OS_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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25链表的节点操作在这里不详细叙事(μC/OS源码该函数下有说明).
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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25链表的节点操作在这里不详细叙事(μC/OS源码该函数下有说明).
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; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33链表的节点操作在这里不详细叙事(μC/OS源码该函数下有说明).
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; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48链表的节点操作在这里不详细叙事(μC/OS源码该函数下有说明).