1、I第 3 章 内核结构 .13.0 临界段(Critical Sections).13.1 任务 .13.2 任务状态 .33.3 任务控制块(Task Control Blocks, OS_TCBs) .43.4 就绪表(Ready List) .73.5 任务调度(Task Scheduling) .103.6 给调度器上锁和开锁(Locking and UnLocking the Scheduler) .113.7 空闲任务(Idle Task) .123.8 统计任务 .133.9 C/OS中的中断处理 .163.10 时钟节拍 .203.11 C/OS-初始化 .233.12 C/O
2、S-的启动 .243.13 获取当前 C/OS- 的版本号 .273-1第 3 章 内核结构本章给出 C/OS-的主要结构概貌。读者将学习以下一些内容; C/OS-是怎样处理临界段代码的; 什么是任务,怎样把用户的任务交给 C/OS-; 任务是怎样调度的; C/OS-是怎样知道应用程序 CPU 的利用率的; 怎样写中断服务子程序; 什么是时钟节拍,C/OS-是怎样处理时钟节拍的; C/OS-是怎样初始化的; 怎样启动多任务;本章还描述以下函数,这些服务于应用程序: OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL(), OSInit(), OSStart(), OS
3、IntEnter() 和 OSIntExit(), OSSchedLock() 和 OSSchedUnlock(), 以及 OSVersion().3.0 临界段(Critical Sections)C/OS-定义两个宏(macros)来关中断和开中断。分别是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。在文件 OS_CPU.H 中。每种微处理器都有自己的 OS_CPU.H 文件。3.1 任务一个任务通常是一个无限的循环L3.1(2),如程序清单 3.1 所示。程序清单 L3.1 任务是一个无限循环void YourTask (void *pdata) (1)
4、for (;) (2)/* 用户代码 */调用uC/OS-II的某种系统服务:OSMboxPend();OSQPend();OSSemPend();OSTaskDel(OS_PRIO_SELF);OSTaskSuspend(OS_PRIO_SELF);OSTimeDly();3-2OSTimeDlyHMSM();/* 用户代码 */当任务完成以后,任务可以自我删除,如清单 L3.2 所示。注意任务代码并非真的删除了,C/OS-只是简单地不再理会这个任务了,这个任务的代码也不会再运行。程序清单 L 3.2 . 任务完成后自我删除void YourTask (void *pdata)/* 用户代码
5、 */OSTaskDel(OS_PRIO_SELF);3.2 任务状态睡眠态(DORMANT):指任务驻留在程序空间之中,还没有交给 C/OS-管理。就绪态:当任务一旦建立,这个任务就进入就绪态准备运行。运行态:调用 OSStart()可以启动多任务。等待状态中断状态图 3.1 任务的状态3-33.3 任务控制块(Task Control Blocks, OS_TCBs)一旦任务建立了,任务控制块 OS_TCBs 将被赋值(程序清单 3.3) 。任务建立的时候,OS_TCBs 就被初始化。程序清单 L 3.3 C/OS-II任务控制块 .typedef struct os_tcb OS_STK
6、 *OSTCBStkPtr;#if OS_TASK_CREATE_EXT_ENvoid *OSTCBExtPtr;OS_STK *OSTCBStkBottom;INT32U OSTCBStkSize;INT16U OSTCBOpt;INT16U OSTCBId;#endifstruct os_tcb *OSTCBNext;struct os_tcb *OSTCBPrev;#if (OS_Q_EN #endif#if (OS_Q_EN #endifINT16U OSTCBDly;INT8U OSTCBStat;INT8U OSTCBPrio;INT8U OSTCBX;INT8U OSTCBY;I
7、NT8U OSTCBBitX;INT8U OSTCBBitY;#if OS_TASK_DEL_ENBOOLEAN OSTCBDelReq;#endif OS_TCB;.OSTCBX, .OSTCBY, .OSTCBBitX 和 .OSTCBBitY 用于加速任务进入就绪态的过程或进入等3-4待事件发生状态的过程(避免在运行中去计算这些值) 。这些值是在任务建立时算好的,或者是在改变任务优先级时算出的。这些值的算法见程序清单 L3.4。程序清单 L 3.4 任务控制块 OS_TCB中几个成员的算法OSTCBY = priority 3;OSTCBBitY = OSMapTblpriority 3
8、;OSTCBX = priority OSTCBBitX = OSMapTblpriority 应用程序中可以有的最多任务数(OS_MAX_TASKS)是在文件 OS_CFG.H 中定义的。这个最多任务数也是 C/OS-分配给用户程序的最多任务控制块 OS_TCBs 的数目。在 C/OS-初始化的时候,如图 3.2 所示,所有任务控制块 OS_TCBs 被链接成单向空任务链表。当任务一旦建立,空任务控制块指针 OSTCBFreeList 指向的任务控制块便赋给了该任务,然后 OSTCBFreeList 的值调整为指向下链表中下一个空的任务控制块。一旦任务被删除,任务控制块就还给空任务链表。图
9、3.2 空任务列表3.4 就绪表(Ready List)每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量 OSRdyGrp 和OSRdyTbl。OSRdyGrp 和 OSRdyTbl之间的关系见图 3.3,是按以下规则给出的:当 OSRdyTbl0中的任何一位是 1 时,OSRdyGrp 的第 0 位置 1,当 OSRdyTbl1中的任何一位是 1 时,OSRdyGrp 的第 1 位置 1,当 OSRdyTbl2中的任何一位是 1 时,OSRdyGrp 的第 2 位置 1,当 OSRdyTbl3中的任何一位是 1 时,OSRdyGrp 的第 3 位置 1,当 OSRdyTbl4中的任何
10、一位是 1 时,OSRdyGrp 的第 4 位置 1,当 OSRdyTbl5中的任何一位是 1 时,OSRdyGrp 的第 5 位置 1,当 OSRdyTbl6中的任何一位是 1 时,OSRdyGrp 的第 6 位置 1,当 OSRdyTbl7中的任何一位是 1 时,OSRdyGrp 的第 7 位置 1,3-5图 3.3C/OS-就绪表程序清单 3.5 中的代码用于将任务放入就绪表。Prio 是任务的优先级。程序清单 L3.5 使任务进入就绪态OSRdyGrp |= OSMapTblprio 3;OSRdyTblprio 3 |= OSMapTblprio 表 T3.1 OSMapTbl的值I
11、ndex Bit Mask (Binary)0 000000011 000000102 000001003 000010004 000100005 001000006 010000007 100000003-6如果一个任务被删除了,则用程序清单 3.6 中的代码做求反处理。程序清单 L3.6 从就绪表中删除一个任务if (OSRdyTblprio 3 为了找到那个进入就绪态的优先级最高的任务,并不需要从 OSRdyTbl0开始扫描整个就绪任务表,只需要查另外一张表,即优先级判定表 OSUnMapTbl256。INT8U const OSUnMapTbl = 0, 0, 1, 0, 2, 0,
12、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, /* 0x4
13、0 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,
14、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 0
15、xDF */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 */;程序清单 L3.7 找出进入就绪态的优先级最高的任务y = OSUnMapTblOSRdyGrp;x = OSUnMapTblOSRdyTbly;prio = (y 0) OSLockNesting-;if (OSLockNesting | OSIntNesting) = 0) (1)OS_EXIT_CRITICAL();
16、OSSched(); (2) else OS_EXIT_CRITICAL(); else OS_EXIT_CRITICAL();3.7 空闲任务(Idle Task)C/OS-总是建立一个空闲任务,在没有其它任务进入就绪态时投入运行。空闲任务设为最低优先级,即 OS_LOWEST_PRI0。程序清单 L3.11 C/OS- 的空闲任务 .void OSTaskIdle (void *pdata)pdata = pdata;for (;) OS_ENTER_CRITICAL();OSIdleCtr+;OS_EXIT_CRITICAL();3.8 统计任务提供运行时间统计的任务叫做 OSTaskS
17、tat()。如果用户将系统定义常数OS_TASK_STAT_EN(见文件 OS_CFG.H)设为 1,这个任务就会建立。一旦得到了允许,OSTaskStat()每秒钟运行一次(见文件 OS_CORE.C) ,计算当前的 CPU 利用率。并放在有符号 8 位整数 OSCPUsage 中。3-9程序清单 L3.12 初始化统计任务 .void main (void)OSInit(); /* 初始化uC/OS-II (1)*/* 安装uC/OS-II的任务切换向量 */* 创建用户起始任务(为了方便讨论,这里以TaskStart()作为起始任务) (2)*/OSStart(); /* 开始多任务调度 (3)*/void TaskStart (void *pdata)/* 安装并启动uC/OS-II的时钟节拍 (4)*/OSStatInit(); /* 初始化统计任务 (5)*/* 创建用户应用程序任务 */for (;) /* 这里是TaskStart()的代码! */图 F3.4 统计任务的初始化