第五章 核武器一 【传宗接代的废话】 μC/OS-II内核有很多重要全局变量,OS的实现离不开它们。鉴于它们在OS核中,我们就称它们为“核武器”。 本章只介绍本册涉及的那部分核武器,并将这部分核武器称为“核武器一”,其余将在中册“核武器二”中介绍。 (为简单起见,本章所介绍的变量部分基于预编译后) · 非结构体全局变量及参数本节介绍的是“轻型核武器” —— 非结构体类全局变量。 #define OS_TICKS_PER_SEC 1000u //定义一秒钟为多少TICKS #define OS_MAX_TASKS 20u //定义用户任务最大数 #define OS_N_SYS_TASKS 2u //定义系统任务总数 #define OS_LOWEST_PRIO 63u //定义最低优先级 #define OS_TASK_IDLE_STK_SIZE 32u //定义统计任务堆栈大小 #define OS_TASK_STAT_PRIO (OS_LOWEST_PRIO - 1u) //定义统计任务优先级为次低 #define OS_TASK_IDLE_PRIO (OS_LOWEST_PRIO) //定义空闲任务优先级为最低 #define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8u + 1u) //定义就绪表(数组)大小 #define OS_TCB_RESERVED ((OS_TCB *)1) //定义保留的TCB标志为1 #define OS_EXT extern //定义OS_EXT表示在外部使用 typedef INT8U OS_PRIO; //定义OS_PRIO类型为INT8U typedef INT32U OS_STK; //定义OS_STK类型为INT32U OS_EXT INT32U OSCtxSwCtr; //定义任务切换计数器。μC/OS-II用“Ctr”后缀表示计数器 OS_EXT INT8U OSIntNesting; //定义中断嵌套数。可作为调度器可否进行调度的标志 //以保证调度器不会在中断服务程序中进行任务调度 OS_EXT INT8U OSLockNesting; //定义锁定嵌套数。可作为调度器可否进行调度的标志 OS_EXT INT8U OSPrioCur; //定义当前任务的优先级 OS_EXT INT8U OSPrioHighRdy; //定义最高就绪任务的优先级 OS_EXT OS_PRIO OSRdyGrp; //定义就绪任务组 //用途:每一位对应的表示每一组OSRdyTbl是否有进入就绪态的任务 OS_EXT OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE]; //定义任务就绪表 //若系统支持64个任务,则把64个任务分为8组,每组8个任务 //每位数据的0、1状态代表64个任务是否处于就绪态 OS_EXT BOOLEAN OSRunning; //定义OS是否运行标志 OS_EXT INT8U OSTaskCtr①; //定义任务计数器 OS_EXT volatile INT32U OSIdleCtr①; //定义空闲任务计数器 OS_EXT OS_STK OSTaskIdleStk①[OS_TASK_IDLE_STK_SIZE]; //定义空闲任务堆栈 OS_EXT OS_STK OSTaskStatStk[OS_TASK_STAT_STK_SIZE]; //定义统计任务堆栈 ------------------------------------------------------------------------------------------------------------------------------------------- ① 从以上代码(“原版”μC/OS代码也同)可以看出,μC/OS开山鼻祖及“引渡者”对变量名的斟酌并没有下太多功夫。 笔者认为μC/OS变量的命名有时不能尽更大程度,让人一目了然,甚至一些变量命名充满矛盾,缺乏规范。 如,本节的OSTaskCtr、OSIdleCtr、OSTaskIdleStk,如果统一,则可以命名为: OSTaskCtr、OSTaskIdleCtr、OSTaskIdleStk。(对于Task,有时带上,有时省去,对用户来说并不友好。) ------------------------------------------------------------------------------------------------------------------------------------------- · 结构体全局变量OS_TCB本节介绍的是“重型核武器” —— 结构体类全局变量。 typedef structos_tcb { OS_STK *OSTCBStkPtr; //任务堆栈指针(指向任务堆栈栈顶),翻译为英文是TaskSP① struct os_tcb *OSTCBNext; //指向下一个任务控制块(在TCBList中)② struct os_tcb *OSTCBPrev; //指向上一个任务控制块(在TCBList中)② INT32U OSTCBDly; //任务等待的节拍数 INT8U OSTCBStat; //任务控制块的状态标志 INT8U OSTCBStatPend; //任务控制块的等待状态标志 INT8U OSTCBPrio; //任务优先级 INT8U OSTCBX; //OSTCBX = priority & 0x07; INT8U OSTCBY; //OSTCBY = priority >> 3; OS_PRIO OSTCBBitX; //OSTCBBitX = OSMapTbl[priority & 0x07]; OS_PRIO OSTCBBitY; //OSTCBBitY= OSMapTbl[priority >> 3]; //提前做了运算并存储,以上四个变量存在的目的是节约任务调度时间 //相关说明参见【第十二章】 } OS_TCB; OS_EXT OS_TCB *OSTCBCur; //定义指向运行任务控制块的指针 OS_EXT OS_TCB *OSTCBFreeList; //定义指向空闲任务控制块列表的指针 OS_EXT OS_TCB *OSTCBHighRdy; //定义指向最高就绪任务控制块的指针 OS_EXT OS_TCB *OSTCBList; //定义指向任务控制块列表的指针 OS_EXT OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1u]; //定义指向优先级任务控制块表③ OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; //定义任务控制块表 //这货只在OS_InitTCBList()中用到 ------------------------------------------------------------------------------------------------------------------------------------------- ① 用“TaskSP”这个命名显然更为合理,那为何“TaskSP”会变成“OSTCBStkPtr”?笔者认为估计是经过了如下演变: ·从“TaskSP”到“OSTCBTaskSP”(因为,μC/OS开山鼻祖及“引渡者”将OS_TCB内所有变量都加了前缀“OSTCB”,这个很合理) ·从“OSTCBTaskSP”到“OSTCBSP”(因为,μC/OS开山鼻祖及“引渡者”编写的代码有时会省去“Task”) ·从“OSTCBSP”到“OSTCBStkPtr”(可能早期,大家喜欢将“StackPointer”简写为“StkPtr”吧,不过,笔者认为,时至今日,“SP”比“StkPtr”优越) 当然,为了整个OS的代码风格统一,“TaskSP”前面加“OSTCB”是必要的,那样,“OSTCBStkPtr”更合理的命名是“OSTCBTaskSP” ② 这个数组里的元素,以任务优先级为0开始,依次存放各个任务控制块的指针,这个数值存在的目的是:在需要时,快速得到相应TCB。 “快速”是因为,只需要通过以下步骤: ·通过函数:OS_SchedNew(),获取就绪任务最高优先级,并将它存给OSPrioHighRdy。 ·通过代码:OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy],将OSTCBHighRdy指向最高就绪任务的TCB。 ③ OSTCBList是一个双向链表,双向链表相比单链表的优点是能够通过Prev访问前一个节点,这也是μC/OS-II采用双链表的原因。 虽然增加任务时,只会将任务TCB从OSTCBList一端插入,那样,将OSTCBList构建为单链表就可以了。 但,删除任务时,删除的TCB节点并不一定在OSTCBList的一端,那样就需要Prev,也就是需用将OSTCBList构建为双链表。 -------------------------------------------------------------------------------------------------------------------------------------------
|