(1)任务控制块
空任务控制块链表OS_init()/任务控制块链表OSTaskCreat()
OS_TCB类型的变量OSTCBCur,用来存放正在运行任务的任务控制块指针。
OSTCBPrioTbl[]任务控制块优先级表(任务调度中使用)
(2)任务就绪表OSRdyTbl[ ]和OSRdyGrp(记录OSRdyTbl中哪个任务组有任务就绪,最多64个任务可以管理)
负责任务调度。这个位置的状态(1或0)来表示任务是否处于就绪状态,1表示就绪;0表示非就绪。
(3)调度器(Scheduler)
任务级的调度是由函数OSSched()完成的。
中断级的调度是由另一个函数OSIntExt()完成的.
由于被中止的任务的指针保存在OS_TCB类型中的OSTCBCur变量中,所以调度器的主要任务就是找到就绪态任务的指针。
OSLockNesting来记录上锁的次数,上锁+1 解锁-1
需要由宏OS_TASK_SW( ) 来引发一次中断或者一次调用来使OSCtxSw( )执行任务切换工作
(4)中断
需要由宏OS_TASK_SW( ) 来引发一次中断或者一次调用来使OSCtxSw( )执行任务切换工作
(4)事件控制块
(5)等待任务列表 OSEventTbl[] 和 OSEventGrp
OSEventTbl[] 和 OSEventGrp 很像前面讲到的OSRdyTbl[]和OSRdyGrp,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
typedef struct
{
void *OSEventPtr; /* 指向消息或者消息队列的指针 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表*/
INT16U OSEventCnt; /* 计数器(当事件是信号量时) */
INT8U OSEventType; /* 事件类型 */
INT8U OSEventGrp; /* 等待任务所在的组 */
} OS_EVENT;
(5)OS_FLAG_GRP来描述信号量集
任务控制块
功能:记录任务的堆栈指针、任务的当前状态、任务的优先级别等一些与任务管理有关的属性。同时OS_TCB负责把代码和任务堆栈进行关联,从而使任务控制块、任务代码和任务堆栈成一个整体。
结构
任务控制块是一个结构类型数据。当用户应程调用OSTaskCreate()函数创建一个用户任务时,这个函数会对任务控制块中的所有成员赋予与该任务相关的数据,并驻留在RAM中。(OSTCBInit())
typedef struct os_tcb {
OS_STK *OSTCBStkPtr;//任务控制块指针
#if OS_TASK_CREATE_EXT_EN //一个任务创建使能
void *OSTCBExtPtr;
OS_STK *OSTCBStkBottom;
INT32U OSTCBStkSize;
INT16U OSTCBOpt;
INT16U OSTCBId;
#endif //OS_MAX_QS 系统中最大消息队列数
struct os_tcb *OSTCBNext; //指向后一个任务控制块的指针
struct os_tcb *OSTCBPrev; //指向前一个任务控制块的指针
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
//有事件使能信号则将指针赋予相应的事件控制块
OS_EVENT *OSTCBEventPtr; //指向事件控制块的指针
#endif
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
void *OSTCBMsg; //指向传递给任务消息的指针
//OSTCBEventPtr和OSTCBMsg分别表示事件控制块的指针和传给任务的消息的指针。
#endif
INT16U OSTCBDly;
INT8U OSTCBStat;
INT8U OSTCBPrio;
INT8U OSTCBX; //用于快速访问就绪表的数据
INT8U OSTCBY;
INT8U OSTCBBitX;
INT8U OSTCBBitY;
#if OS_TASK_DEL_EN
BOOLEAN OSTCBDelReq; //请求删除任务时用到的标志
#endif
} OS_TCB;
OSTCBStat用来存放任务的当前状态:
OS_STAT_RDY 就绪态
OS_STAT_SEM 等待信号量的状态
OS_STAT_MBOX 等待消息邮箱状态
OS_STAT_Q 等待消息队列状态
OS_STAT_SUSPEND 挂起状态
OS_STAT_MUTEX 等待互斥型信号量状态
任务控制块链表(两条)
空任务块链表(其中所有任务控制块还没有分配给任务);OS_init()初始化建立。
任务块链表(已经分配)OSTaskCreat()初始化建立
(1)空任务块链表 调用OSinit即初始化空链表
此链表是在应用程序调用函数OSInit()对UC/OS-II系统进行初始化时建立的。
系统在调用函数OSInit()函数OSInit()时,先在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],这样每个数组元素就是一个任务控制块,然后把这些控制块链接成一个链表,此链表中的任务控制块还没有与具体任务相关联。
上图可以看到UCOS初始化时建立的空任务链表的元素一共是(OS_MAX_TASKS+OS_N_SYS_TASKS)个,也就是有这么多个任务块。
OS_MAX_TASKS(OS_CFG.H中) 表示用户任务的最大数目;
OS_N_SYS_TASKS(UCOS_II.H中)表示系统任务的数目,一般为2,也就是两个系统任务(空闲和统计)。
(2)任务控制块链表
任务块链表是在调用OSTaskCreate()创建任务时建立的。建立任务控制块链表的具体做法是:从空任务控制块链表摘取一个空任务控制块,然后填充上任务属性后,再形成新的链表。
每当应用程序调用OSTaskCreate()和OSTaskCreateExt()创建一个新任务时,系统就将空任务控制块链表头指针OSTCBFreeList指向的任务控制块分配给该任务。在给任务控制块中的个成员赋值后,就按任务控制块链表的头指针OSTCBList将其加入到任务控制块链表中。
其他说明:
为了UC/OS能随时访问正在运行任务的任务控制块,定义了一个
OS_TCB类型的变量OSTCBCur,用来存放正在运行任务的任务控制块指针。
用函数OSTaskDel()删除一个任务,其实质就是把该任务的任务控制块从链表中删掉,并把它归还给空任务控制块链表
任务控制块的初始化:
当应用程序调用OSTaskCreate()创建一个任务时,这个函数会调用系统函数OSTCBInit()来为任务控制块进行初始化。这个函数首先被创建任务从空任务控制块链表获取一个任务控制块;然后用任务的属性对任务控制块各个成员进行赋值;最后把这个任务控制块链入到任务控制块链表的头部。 从空任务控制块链表中取得任务控制块,在加入到任务控制块链表中
OSTCBInit()的原型:
INT8U OSTCBInit(
INT8U prio, //优先级别
OS_STK *pros, //堆栈栈顶指针
OS_STK *pbos, //堆栈栈低指针
INT16U id, //表示符
INT16U stk_size, //堆栈的长度
void *pext, //任务控制块的扩展指针
INT16U opt //任务控制块的选择项
)
四、任务就绪表
任务调度的依据是任务就绪表!(重要性)
定义:
UC/OS-II在RAM中设立了一个记录表,系统中的每个任务都在这个表中占据一个位置,并用这个位置的状态(1或0)来表示任务是否处于就绪状态。这个表就叫任务就绪状态表。
任务就绪表
用一个类型为INT8U的数组OSRdyTbl[ ]来充当任务就绪表。由于每个任务的就绪状态只占据一位, OSRdyTbl[ ]数组的一个元素就可以表达8个任务的就绪状态(1表示就绪;0表示非就绪)。
为了便于对就绪表的查找,UC/OS-II又定义了一个数据类型为INT8U的变量OSRdyGrp,并使该变量的每一个位都对应OSRdyTbl[ ]的一个任务组,即数组的一个元素。如果某任务组中有任务就绪,就在变量OSRdyGrp里把该任务组所对应的位置1;否则置0。
Eg: OSRdyGrp=11100101 表示OSRdyTbl[0]、[2]、[5]、[6]、[7]任务组
中有任务就绪。
由于变量OSRdyGrp有8个二进制位,每位对应OSRdyTbl[]数组的一个元素,而每个元素又可以记录8个任务的就绪状态,因此最多可以管理64个任务
为加快访问任务就绪表的速度,系统定义了一个变量OSRdyGrp来表明就绪表,每行中是否存在就绪任务
如何根据任务的优先级来查找任务在就绪表中的位置
把优先级别看成一个6位的二进制数,用它的高3位指明变量OSRdyGrp的具体数据,确定数组元素;用它的低三位指明该数组元素中的具体数据位。
举例:
优先级为30的一个就绪任务,判断它在就绪表的那个位置置1.
对任务就绪表的操作
找出进入就绪态的优先级最高的任务
y = OSUnMapTbl[ OSRdyGrp ];
x = OSUnMapTbl[ OSRdyTbl[y] ];
prio = (y << 3) + x;
例如,如果OSRdyGrp的值为二进制01101000,查OSUnMapTbl[OSRdyGrp]得到的值是3,它相应于OSRdyGrp中的第3位bit3,这里假设最右边的一位是第0位bit0。类似地,如果OSRdyTbl[3]的值是二进制11100100,则OSUnMapTbl[OSRdyTbl[3]]的值是2,即第2位。于是任务的优先级Prio就等于26(3*8+2)。利用这个优先级的值。查任务控制块优先级表OSTCBPrioTbl[],得到指向相应任务的任务控制块OS_TCB的工作就完成了。
五、任务的调度
μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。
任务调度器的主要工作:
(1)在就绪表中查找具有最高优先级别的就绪任务。
(2)实现任务的切换。
任务级的调度是由函数OSSched()完成的。
中断级的调度是由另一个函数OSIntExt()完成的.
任务切换步骤:
获得待运行就绪任务控制块的指针
调度器实施任务切换之前的主要工作时:获得待运行任务的TCB指针和当前任务的TCB指针。因为被中止任务的任务控制块指针存放在全局变量OSTCBCur(存放正在运行的任务的任务卡的指针)中。此变量里存储的就是当前运行任务的任务控制块指针。所以调度器这块的工作主要就是获得待运行任务的TCB指针。
下面是任务级调度器函数OSSched()源代码
void OSSched (void)
{ INT8U y;
OS_ENTER_CRITICAL();