Ucos源码分析
1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结
1. 任务控制块
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; //当前栈顶指针
struct os_tcb *OSTCBNext; //下一任务控制块指针
struct os_tcb *OSTCBPrev; //上一任务控制块指针
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
OS_EVENT *OSTCBEventPtr; //任务事件控制块
#endif
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
void *OSTCBMsg; //任务消息控制块 与邮箱和队列有关
#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
OS_FLAG_NODE *OSTCBFlagNode; //与事件标志组有关
#endif
OS_FLAGS OSTCBFlagsRdy; //与事件标志组有关
#endif
INT16U OSTCBDly; //任务延时等待变量
INT8U OSTCBStat; //任务状态
INT8U OSTCBPrio; //任务优先级
INT8U OSTCBX; //用于优先级的快速计算
INT8U OSTCBY;
INT8U OSTCBBitX;
INT8U OSTCBBitY;
#if OS_TASK_DEL_EN > 0
BOOLEAN OSTCBDelReq;
#endif
} OS_TCB;
UCOS是通过任务控制块来管理任务的。任务控制块是一个基于链表的数据结构,基本成员如下
控制块成员 | |
---|---|
OSTCBStkPtr | 指向当前任务栈栈顶的指针。μC/OS-Ⅱ允许每个任务有自己的栈,每个任务的堆栈的容量可以是任意的。 |
OSTCBPre | 指向上一个任务控制块指针。用于任务控制块OS_TCB的链接。 |
OSTCBNext | 指向下一个任务控制块指针。用于任务控制块OS_TCB的链接。 |
OSTCBDly | 任务等待的延时时间变量。用于将任务挂起一段时间以等待某事件的发生,这种等待是有超时限制的。 |
OSTCBStat | 任务的当前状态标志变量。其为0时,任务进入就绪态 。 |
OSTCBPrio | 任务优先级变量。变量值越小,任务的优先级越高。 |
关于其中的OSTCBStat成员变量
宏名 | 定义值 | 说明 |
---|---|---|
OS_STAT_RDY | 0x00 | 运行准备就绪 |
OS_STAT_SEM | 0x01 | 在信号量时挂起 |
OS_STAT_MBOX | 0x02 | 在邮箱时挂起 |
OS_STAT_Q | 0x04 | 在队列时挂起 |
OS_STAT_SUSPEND | 0x08 | 任务被手动挂起暂停 |
OS_STAT_MUTEX | 0x10 | 在互斥信号量时挂起 |
OS_STAT_FLAG | 0x20 | 在事件标志组时挂起 |
2. 与任务控制有关的变量
4个指针、1个数组、1个指针数组
OSTCBCur | 指向“当前任务控制块”的指针 |
OSTCBHighRdy | 指向“将要运行最高优先级任务控制块”的指针 |
OSTCBFreeList | “空任务控制块”链表的表头指针 |
OSTCBList | “已使用控制块”链表的表头指针 |
OSTCBTbl[] | 任务控制块数组,所有的任务控制块都保存在这个数组中 |
OSTCBPrioTbl[] | 任务控制块优先级表,存放指向各任务控制块的指针。并按任务的优先级别将这些指针存放在数组的各个元素里 |
几个变量之间的关系
3.任务就绪表的放入与移除
相关变量:任务优先级Prio、任务就绪组OSRdyGrp、任务就绪表OSRdyTbl[ ]
**任务优先级表是8*8共64个,每一个8可以用任务优先级的3位来表示
需用用到一个映射表
映射表的作用,用以确定将哪一位置1
INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
任务就绪表的放入
OSRdyGrp |= OSMapTbl[Prio >> 3];
OSRdyTbl[Prio >> 3] |= OSMapTbl[Prio & 0x07];
任务就绪表的移除
移除时需要确定,移除任务之后,这个任务就绪组是否为0
If ((OSRdyTbl[Prio >> 3] &= ~OSMapTbl[Prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[Prio>>3];
举例:
4.查找最高优先级的任务
采用的是是查表法,是通过解码映射表实现的。
功能是通过找到第一个出现1的位置编码的,下面是几个例子(0 :0X00 默认是0 )
1:0000 0001 第一个1的位置是0
8: 0000 1000 第一个1的位置是3
10: 0000 1010 第一个1的位置是1
所在任务的Y值越小优先级越高,所在任务的X值越小优先级越高,由此可见最小的Y、X值所对应的任务就是进入就绪态优先级最高的的任务。
INT8U const OSUnMapTbl[] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
查找当前任务就绪表中最高优先级的任务
y = OSUnMapTbl[OSRdyGrp]; //查找哪一行先被置1
x = OSUnMapTbl[OSRdyTbl[y]]; //查找该行的哪一列先被置1
Prio = (y << 3) + x; //反推任务优先级
在任务控制块定义的 OSTCBX; OSTCBY; OSTCBBitX; OSTCBBitY;
可以快速获取任务优先级并改变;
就绪组和就绪表的修改依靠OSTCBBitY/X
OSTCBCur->OSTCBY//就绪表的索引
OSRdyGrp = OSTCBCur->OSTCBBitY //修改就绪组 OSRdyGrp
OSRdyTbl[OSTCBCur->OSTCBY] = OSTCBCur->OSTCBBitX//修改就绪表OSRdyTbl
关于最高任务优先级的查找:-----参考文章
有些处理器架构可以支持硬件查找,此时查找更快速
5.系统调度
OS_Sched ():系统调度函数
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
INT8U y;
//进入临界区
OS_ENTER_CRITICAL();
//OSIntNesting == 0判断是否在中断中进行任务调度
if ((OSIntNesting == 0) && (OSLockNesting == 0)) { /* Sched. only if all ISRs done & not locked */
//获取就绪表中此时任务优先级最高的任务
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
//如果将要运行的任务优先级的任务 和 当前任务的任务优先级不同
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
//获取要运行的任务优先级的任务的任务控制块
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
//保存任务上下文进行任务调度
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
OS_EXIT_CRITICAL();
}
void OSTimeTick (void):滴答中断
**滴答中断中只存在将任务放入到就绪表中,不存在任务调度
**当任务等待事件阻塞超时后,任务状态为等待事件状态,任务却在任务就绪表中
void OSTimeTick (void)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
OS_TCB *ptcb;
//提供给外部的钩子函数
OSTimeTickHook(); /* Call user definable hook */
#if OS_TIME_GET_SET_EN > 0
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
//系统时间更新
OSTime++;
OS_EXIT_CRITICAL();
#endif
//系统处于运行状态
if (OSRunning == TRUE) {
//获取已经使用的任务控制块链表的表头
ptcb = OSTCBList; /* Point at first TCB in TCB list */
//遍历所有的任务
//直到空闲任务,
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
//任务延时-1
if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */
//如果任务超时了
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
//判断任务是否是主动挂起
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */
//任务不是被主动挂起的,需要将其加入到就绪表
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make task R-to-R (timed out)*/
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
} else { /* Yes, Leave 1 tick to prevent ... */
//判断任务是主动挂起的重新设置延时为1
ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */
} /* ... suspension is removed. */
}
}
//下一个任务的任务控制块
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
}
}