μCOS-II微小内核分析_第1页
μCOS-II微小内核分析_第2页
μCOS-II微小内核分析_第3页
μCOS-II微小内核分析_第4页
μCOS-II微小内核分析_第5页
已阅读5页,还剩143页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C/OS-II微小内核分析 JAN,C/OS-II微小内核分析微小内核分析 绪论绪论 C/OS-II微小内核是为了方便初学者学习嵌入式实时操作系统的基本原理,分别由小到大将C/OS-II V2.52裁减为几个只具备基本功能的微小内核。建议:初学者学习或者教师讲授本章内容时,一定要尽量做到一边阅读源代码一边画图,事实上“过程比结论更重要” !1100行(剔除文件头和函数头后的数目) 只有418行(剔除文件头和函数头后的数目),仅包含5个最基本的服务函数的“最小内核最小内核”。 目 录概述概述 临界区与中断管理临界区与中断管理 最小内核最小内核 1任务的结束任务的结束 信号量信号量2345C/OS

2、-II微小内核分析微小内核分析 概述概述C/OS-II微小内核简介 函数说明 12C/OS-II体系结构 概述概述| C/OS-II微小内核分析微小内核分析 C/OS-II微小内核简介微小内核简介 C/OS-II微小内核源代码分为3部分: 移植代码 配置文件 内核代码概述概述| C/OS-II微小内核分析微小内核分析 C/OS-II微小内核简介微小内核简介 1、内核代码(source) OS_CORE.COS_TASK.COS_TIME.CuCOS_II.HOS_SEM.CSOURCE1SOURCE2带删除任务 SOURCE3 带删除任务SOURCE4 带删除任务带删除信号量(1) OS_CO

3、RE.C:OS内核文件,提供一些内核最基本的服务和不好分类的服务;(2) OS_TASK.C:任务管理文件,提供任务管理服务;(3) OS_TIME.C: 时间管理文件,提供系统延时服务;(4) uCOS_II.H: 头文件,定义各种数据结构、常量、变量和声明各种函数和变量;(5) OS_SEM.C: 信号量管理文件,提供任务间通讯服务。概述概述| C/OS-II微小内核分析微小内核分析 C/OS-II微小内核简介微小内核简介 2、移植代码(1) OS_CPU_C.C:移植代码C语言部分;(2) OS_CPU_a.S:移植代码汇编语言部分;(3) OS_CPU.H:移植代码头文件;(4) IR

4、Q.INC:移植代码与芯片无关的中断处理接口程序。 本书仅提供基于ARM的移植代码,当然所有基于C/OS-II V2.52的移植代码均可在本书提供的C/OS-II微小内核上使用。基于ARM的移植代码位于arm目录下,分别为OS_CPU_C.C、OS_CPU_a.S、OS_CPU.H和IRQ.INC 4个文件。概述概述| C/OS-II微小内核分析微小内核分析 C/OS-II微小内核简介微小内核简介 3、配置文件 (1) INCLUDES.H:内核需要的头文件,对于特定的移植,一般不需要改变;(2)OS_CFG.H:内核配置的头文件,一般需要根据程序的需求修改其常量的内容。 配置文件是每个C/O

5、S-II程序必备的文件,而且不同的程序一般不一样,但大小基本上相同。配置文件范例位于H目录下,分别为INCLUDES.H和OS_CFG.H文件。 一般来说,每个应用程序都有自己的配置文件拷贝,并很可能与范例不同。概述概述| C/OS-II微小内核分析微小内核分析 函数说明函数说明 功能最强的内核SOURCE4提供了14个用户接口函数详见下表。函数名称OSInit所属文件OS_CORE.C函数原型void OSInit(void)功能描述初始化C/OS-,无函数参数和返回值特殊说明必须在调用OSStart()函数之前调用OSInit(),而只有在调用OSStart()函数之后,C/OS-才真正开

6、始运行多任务OSInit函数函数名称OSStart 所属文件OS_CORE.C函数原型void OSStart(void) 功能描述启动C/OS-II的多任务环境,无函数参数和返回值 特殊说明在调用OSStart( )之前必须先调用OSInit ( )。在用户程序中OSStart( )只能被调用一次,第二次调用OSStart( )将不执行任何操作 OSStart函数函数名称OSTaskCreate 所属文件OS_TASK.C 函数原型INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio

7、) 功能描述建立一个新任务。既可以在多任务环境启动之前,也可以在正在运行的任务中创建任务 函数参数task :指向任务代码的指针(函数指针)pdata :传递给任务的参数(一个变量指针)ptos :指向任务堆栈栈顶的指针prio :任务的优先级 函数返回值OS_NO_ERR:函数调用成功OS_PRIO_EXIST:具有该优先级的任务已经存在OS_PRIO_INVALID:参数指定的优先级大于OS_LOWEST_PRIOOS_NO_MORE_TCB:系统中没有OS_TCB可以分配给任务了 特殊说明任务堆栈必须声明为OS_STK类型。注意:在中断处理程序中不能建立任务。在任务中必须调用C/OS提供

8、的下述过程之一:延时等待、任务挂起、等待事件发生(等待信号量,消息邮箱、消息队列),以便其它任务也能获得CPU的使用权 OSTaskCreate函数函数名称OSTimeDly 所属文件OS_TIME.C 函数原型void OSTimeDly (INT16U ticks) 功能描述将一个任务延时若干个时钟节拍,无函数返回值。延时时间的长度可从0到65535个时钟节拍,延时时间0表示不进行延时,函数将立即返回调用者,延时的具体时间依赖于系统每秒钟有多少时钟节拍(由文件OS_CFG.H中的常量OS_TICKS_PER_SEC设定)函数参数ticks:要延时的时钟节拍数 特殊说明延时时间0表示不进行延

9、时操作,而立即返回调用者。为了确保设定的延时时间,建议用户设定的时钟节拍数加1。例如,希望延时10个时钟节拍,可设定参数为11 OSTimeDly函数函数名称OSTimeTick 所属文件OS_CORE.C 函数原型void OSTimeTick(void) 功能描述在每次时钟节拍中断服务程序中被调用,无函数参数和返回值。OSTimeTick()检查处于延时状态的任务是否达到延时时间,或正在等待事件的任务是否超时 特殊说明OSTimeTick()的运行时间和系统中的任务数直接相关,在任务或中断中都可以调用。如果在任务中调用,任务的优先级应该很高(优先级数字很小),这是因为OSTimeTick(

10、)负责所有任务的延时操作 OSTimeTick函数函数名称OSTaskDel 所属文件OS_TASK.C 函数原型INT8U OSTaskDel (INT8U prio) 功能描述删除一个指定优先级的任务。被删除的任务将回到休眠状态,任务被删除后可以用函数OSTaskCreate()重新建立 函数参数prio :指定要删除任务的优先级,如果为OS_PRIO_SELF则删除自身 函数返回值OS_NO_ERR:函数调用成功OS_TASK_DEL_IDLE:错误,试图删除空闲任务(Idle task)OS_TASK_DEL_ ERR:错误,指定要删除的任务不存在OS_PRIO_INVALID:参数指

11、定的优先级大于OS_LOWEST_PRIOOS_TASK_DEL_ISR:错误,试图在中断处理程序中删除任务 OSTaskDel函数函数名称OSIntEnter 所属文件OS_CORE.C 函数原型void OSIntEnter (void) 功能描述通知C/OS-一个中断服务已开始执行,这有助于C/OS-掌握中断嵌套的情况。通常OSIntExit()和OSIntEnter()联合使用,无函数参数和返回值 特殊说明在中断服务程序中,如果保证直接递增OSIntNesting“原子操作原子操作”,中断服务程序使用直接递增OSIntNesting的方法而不调用OSIntEnter()函数。何为原子操

12、作?在一个任务的执行过程中,如果有某些操作不希望在执行何为原子操作?在一个任务的执行过程中,如果有某些操作不希望在执行过程中被别的任务或中断打断,那么这些不希望被打断的操作就是原子操作过程中被别的任务或中断打断,那么这些不希望被打断的操作就是原子操作 OSIntEnter函数函数名称OSIntExit 所属文件OS_CORE.C 函数原型void OSIntExit(void) 功能描述通知C/OS-一个中断服务已执行完毕,这有助于C/OS-掌握中断嵌套的情况。通常OSIntExit()和OSIntEnter()联合使用。当最后一层嵌套的中断执行完毕后,如果有更高优先级的任务准备就绪,C/OS

13、-会调用任务调度函数,在这种情况下,中断返回到更高优先级的任务而不是被中断了的任务。无函数参数和返回值 特殊说明在任务级不能调用该函数。即使中断服务程序使用直接递增OSIntNesting的方法(没有调用OSIntEnter()),也必须调用OSIntExit()函数 OSIntExit函数函数名称OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL() 所属文件移植代码 函数原型由移植代码决定 功能描述一般来说,OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()为定义的宏,用来禁止、打开CPU的中断,无函数参数和返回值 特殊说明OS_ENTER_

14、CRITICAL()和OS_EXIT_CRITICAL()必须成对使用 禁止/允许中断函数函数名称OSSemCreate 所属文件OS_SEM.C 函数原型OS_EVENT *OSSemCreate (INT16U cnt) 功能描述建立并初始化一个信号量 函数参数cnt :建立的信号量的初始值,可以取0到65535之间的任何值 函数返回值正常 : 指向分配给所建立的信号量的事件控制块的指针NULL :没有可用的事件控制块 特殊说明必须先建立信号量,然后使用 OSSemCreate函数函数名称OSSemPend 所属文件OS_SEM.C 函数原型void OSSemPend (OS_EVENT

15、 *pevent, INT16U timeout, INT8U *err) 功能描述等待信号量:当任务调用OSSemPend()函数时,如果信号量的值大于零,那么OSSemPend()函数对该值减一并返回:如果调用时信号量等于零,那么OSSemPend()函数将任务加入该信号量的等待列表,任务将等待直到获得信号量或超时 函数参数pevent :指向信号量的指针,OSSemCreate()的返回值timeout:最多等待的时间(超时时间),以时钟节拍为单位err :用于返回错误码 函数返回值*err可能为以下值:OS_NO_ERR :调用成功,得到信号量OS_TIMEOUT :超过等待时间OS_

16、ERR_PEND_ISR :在中断中调用该函数所引起的错误OS_ERR_EVENT_TYPE :错误,pevent 不是指向信号量的指针OS_ERR_PEVENT_NULL :错误,pevent为NULL 特殊说明必须先建立信号量,然后使用,不允许在中断中调用该函数,因为中断不能被挂起 OSSemPend函数函数名称OSSemPost 所属文件OS_SEM.C 函数原型INT8U OSSemPost (OS_EVENT *pevent) 功能描述发送信号量:如果指定的信号量是零或大于零,OSSemPost()函数递增该信号量并返回。如果有任务在等待信号量,则最高优先级的任务将得到信号量并进入就

17、绪状态。然后进行任务调度,决定当前运行的任务是否仍然为处于最高优先级的就绪态的任务 函数参数pevent :指向信号量的指针,OSSemCreate()的返回值 函数返回值OS_NO_ERR :发送信号量成功OS_SEM_OVF :信号量的值溢出OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针OS_ERR_PEVENT_NULL :错误,pevent为NULL 特殊说明必须先建立信号量,然后使用 OSSemPost函数函数名称OSSemDel 所属文件OS_SEM.C 函数原型OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt

18、, INT8U *err) 功能描述删除信号量:在删除信号量之前,应当删除可能会使用这个信号量的任务 函数参数pevent :指向信号量的指针,OSSemCreate()的返回值opt :定义信号量删除条件OS_DEL_NO_PEND:没有任何任务等待信号量才删除OS_DEL_ALWAYS:立即删除err :用于返回错误码 函数返回值*err可能为以下值:OS_NO_ERR :成功删除信号量OS_ERR_DEL_ISR :在中断中删除信号量所引起的错误OS_ERR_INVALID_OPT :错误,opt值非法OS_ERR_TASK_WAITING :有一个或多个任务在等待信号量OS_ERR_E

19、VENT_TYPE :错误,pevent不是指向信号量的指针OS_ERR_PEVENT_NULL :错误,pevent为NULL 特殊说明(1)使用这个函数调用时,必须特别小心,因为其它任务可能还要用这个信号量(2)当挂起任务就绪时,中断关闭时间与挂起任务数目有关(3)其它任务并不知道信号量被删除,除非检查pevent是否指向NULL OSSemDel函数C/OS-II微小内核分析微小内核分析 最小内核最小内核 本节以最小的C/OS-II微小内核SOURCE1 中5个主要服务函数OSInit()、OSStart()、OSTaskCreate()、OSTimeDly()和OSTimeTick()

20、为基础展开分析,揭开C/OS-II 实时操作系统(RTOS)的神秘面纱。 C/OS-II微小内核分析微小内核分析 最小内核最小内核 基本概念案例分析 12任务控制块(OS_TCB)3获得并初始化一个OS_TCBTargetInit初始化启动OS8910任务就绪算法OS初始化45任务管理6时间管理SWI软件中断异常任务调度111213任务堆栈初始化7任务级的任务调度小结14最小内核最小内核 | C/OS-II微小内核分析微小内核分析 基本概念基本概念 并行并行程序执行的独立独立的基本逻辑单位”称之为“任务”。1、什么是任务 非独立的任务结构 独立的任务结构 独立的具体含义独立的具体含义最小内核最

21、小内核 | C/OS-II微小内核分析微小内核分析 基本概念基本概念 可并行执行的单位可并行执行的单位 可以认为C/OS-II为每个任务虚拟了一个CPU。在对任务进行编程时,应该时刻谨记这个观点。 虚拟了多个CPU 等待状态:等待状态:任务在等待某一事件的发生还不能运行的状态;被中断状态:被中断状态:因为中断服务程序正在执行而得不到CPU控制权的状态;睡眠状态:睡眠状态:任务驻留在程序空间之中,还没有交给C/OS-管理的状态,而将任务交给C/OS-管理是通过调用函数OSTaskCreate()实现的;就绪状态:就绪状态:任务已经准备好,可以运行了,但由于该任务的优先级比正在运行的任务的优先级低

22、,还暂时不能运行的状态。在C/OS-中,当任务一旦建立,这个任务就进入就绪态准备运行;运行状态:运行状态:任务掌握了CPU的控制权,正在运行中的状态。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 基本概念基本概念2、任务的状态 任务状态描述最小内核最小内核 | C/OS-II微小内核分析微小内核分析 基本概念基本概念任务状态切换最小内核最小内核 | C/OS-II微小内核分析微小内核分析 案例分析案例分析 LPC2000P0.9R3470P0.10R1470LED1LED2VCC 在嵌入式系统中,经常需要完成几件互不相关或关系不大的任务。如控制空调的风速、风向和温度,实质上就是

23、控制不同的电机,可以说它们是几个不相关联的任务。我们不妨用2个LED来模拟2个控制对象,让它们以不同的速度闪烁,LED低电平点亮、高电平熄灭。1、电路原理图 本章除非特别说明,否则默认以此为范例 。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 案例分析案例分析 主程序开始uC/OS-II初始化建立任务启动多任务环境(让任务开始执行)结束(永远不可能执行到这里)任务0初始化目标板初始化GPIO点亮LED1等待1/4秒熄灭LED1等待1/4秒任务1初始化GPIO点亮LED2等待1/3秒熄灭LED2等待1/3秒2、程序流程图 int main (void) OSInit(); OST

24、askCreate(Task0,(void *)0, &TaskStk0TaskStkLengh1, 4); OSTaskCreate(Task1,(void *)0, &TaskStk1TaskStkLengh1, 5); OSStart(); return 0; 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 案例分析案例分析 C/OS-II初始化创建2个任务启动C/OS-II,任务开始运行3、程序代码 main函数main()函数与各任务之间关联工作都是由操作系统来完成的,阅读OS的源代码是探索其中奥秘最好的方法。 void Task0 (void *pda

25、ta) pdata = pdata; TargetInit(); IO0DIR |= LED1; while (1) IO0CLR = LED1; OSTimeDly(OS_TICKS_PER_SEC / 4); IO0SET = LED1; OSTimeDly(OS_TICKS_PER_SEC / 4); 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 案例分析案例分析 目标初始化熄灭LED1 用户任务代码初始化GPIO点亮LED1等待1/4秒等待1/4秒在进入首个运行的任务之前要禁止产生任何受操作系统管理的中断,包括节拍定时器的中断。所以在目标初始化中才初始化中断系统,保证在

26、多任务启动之前不产生中断。void Task1(void *pdata) pdata = pdata; IO0DIR |= LED2; while (1) IO0CLR = LED2; OSTimeDly(OS_TICKS_PER_SEC / 3); IO0SET = LED2; OSTimeDly(OS_TICKS_PER_SEC / 3); 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 案例分析案例分析 目标初始化熄灭LED2 用户任务代码初始化GPIO点亮LED2等待1/3秒等待1/3秒最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(

27、OS_TCB) C/OS-是通过任务控制块来管理任务的,实际上创建任务就是给任务代码分配一个任务控制块。任务控制块OS_TCB是一个基于链表的数据结构。 typedef struct os_tcb OS_STK *OSTCBStkPtr; struct os_tcb *OSTCBNext; INT16U OSTCBDly; INT8U OSTCBStat; INT8U OSTCBPrio; INT8U OSTCBX; INT8U OSTCBY; INT8U OSTCBBitX; INT8U OSTCBBitY; OS_TCB; 指向任务堆栈栈顶的指针任务的优先级别指向后一个任务控制块的指针任务

28、等待的延迟时间(节拍数)任务的当前状态标志TCB部分变量赋值算法,为加快计算速度而添加的成员非必须成员#define OS_STAT_RDY0 x00#define OS_STAT_SEM0 x01#define OS_STAT_MBOX0 x02#define OS_STAT_Q0 x04 #define OS_STAT_SUSPEND0 x08#define OS_STAT_MUTEX0 x10#define OS_STAT_FLAG0 x20最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) typedef struct os_tcb O

29、S_STK *OSTCBStkPtr; struct os_tcb *OSTCBNext; INT16U OSTCBDly; INT8U OSTCBStat; INT8U OSTCBPrio; . OS_TCB; 指向任务堆栈栈顶的指针任务的优先级别指向后一个任务控制块的指针任务等待的延迟时间(节拍数)任务的当前状态标志C/OS-允许每个任务有自己的堆栈,而且每个任务堆栈的容量可以是任意大小的。OSTCBStkPtr是OS_TCB数据结构中唯一能用汇编语言来处置的变量,OSTCBStkPtr放在数据结构的最前面,即数组的首元素,从而用汇编语言来处理这个变量非常容易(在偏移量为0的位置)。 用于

30、任务控制块OS_TCB的链接。该链表在OSTimeTick()时钟节拍函数中使用,用于刷新各个任务的任务延迟变量OSTCBDly,每个任务的OS_TCB任务控制块在任务建立时就已经被链接到链表中了。 当需要将任务延时若干个时钟节拍时要用到这个变量,或者需要将任务挂起一段时间以等待某个事件的发生,这种等待是有超时限制的。在这种情况下,这个变量保存的是任务允许等待事件发生的最多时钟节拍数。如果这个变量为0,表示任务不延时,或者表示等待事件发生的时间没有限制,即一直等待。任务的状态字,用于存放任务的当前状态。 运行准备就绪任务被暂停在信号量时挂起在邮箱时挂起在队列时挂起在事件标志组时挂起在互斥信号量

31、时挂起任务优先级。任务的优先级越高,则OSTCBPrio值就越小。 OS_EXT OS_TCB *OSTCBCur; OS_EXT OS_TCB *OSTCBFreeList; OS_EXT OS_TCB *OSTCBHighRdy;OS_EXT OS_TCB *OSTCBList; OS_EXT OS_TCB *OSTCBPrioTblOS_LOWEST_PRIO + 1; OS_EXT OS_TCB OSTCBTblOS_MAX_TASKS+OS_N_SYS_TASKS;指向当前运行的TCB的指针最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_T

32、CB) 指向空闲TCB链表的指针指向将要运行的任务控制块的指针总是指向最后建立的任务的指针OSTCBCur是专门存放“当前任务控制块”的指针,可随时、快速访问正在运行的任务的任务控制块。 在C/OS-中定义了几个与任务控制块相关的数组和指针, C/OS-最小内核定义了一个数组、4个指针和一个指针数组 。指向优先级任务控制块用于分配TCB空间一旦任务建立时,空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务,然后OSTCBFreeList的值便调整为指向链表中下一个空的任务块 。 指向“将要运行最高优先级任务控制块”的指针。 指向“已使用任务控制块”链表的头指针。当正式创建

33、一个任务时,OSTCBList总是指向最后建立的任务。 任务控制块优先级表,专门用来存放指向各任务控制块的指针,并按任务的优先级别将这些指针存放在数组的各个元素里。这样系统在访问一个任务的任务控制块时,就不必遍历任务控制块链表了。只要知道任务的优先级,就可以迅速地从该数组中找到它的任务控制块。 用于分配任务控制块所需空间的数组,所有的任务控制块都保存在这个数组中。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) C/OS-总是将最低优先级OS_LOWEST_PRIO分配给“空闲任务”(OS_TaskIdle),在此例中,最低优先级设定为9,则

34、“空闲任务”的优先级为9,用户实际可使用的优先级资源为08共9个,其中Task0的优先级为4,Task1的优先级为5。C/OS-还保留最高的4个优先级(0、1、2、3),保留OS_LOWEST_PRIO1(即优先级8)给“统计任务”(注意:C/OS-最小内核无统计任务,完整版可配置为有或者无统计任务),OS_LOWEST_PRIO2(即优先级7)和OS_LOWEST_PRIO3(即优先级6)的使用权,以备将来OS升级时使用。define OS_LOWEST_PRIO 9 C/OS-共有64个优先级,优先级的高低按照编号从0(最高)到63(最低)排序。为节约系统资源,可以通过定义系统常量OS_L

35、OWEST_PRIO的值来限制优先级编号的范围。任务控制块优先级表 OSTCBPrioTbl最低优先级设定为9最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) 任务控制块优先级表图解 优先级表任务控制块数组的起始地址任务控制块数组的起始地址 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) 任务控制块数组OSTCBTbl 用于分配任务控制块所需空间的数组,所有的任务控制块都保存在这个数组中。 C/OS-用2个链表来管理任务控制块,一个是空任务块链表,另一个是任务控制块链表。空任务块链表是在调

36、用函数OSInit()对C/OS-进行初始化时建立的,而任务控制块链表是在调用函数OSTaskCreate()创建任务时建立的。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) C/OS-在初始化时建立的“空任务块链表”中的元素一共是OS_MAX_TASKS + OS_N_SYS_TASKS个,即0OS_MAX_TASKS + OS_N_SYS_TASKS1。表尾单向空任务块链表 OSTCBFreeList为空任务控制块链表的“头指针” OS_MAX_TASKS指明了应用程序中可以有的最多任务数; OS_N_SYS_TASKS指明了系统任务的

37、数目 。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) OSTCBFreeList指向OSTCBTb13结点多任务环境刚建立时的任务控制块状态 指向最后建立任务的OSTCBTb12结点指向最高优先级任务的OSTCBTb11结点指向最低优先级任务的OSTCBTb10结点 任务控制块链表是在调用函数OSTaskCreate()创建任务时建立的。在建立多个任务之后控制块的状态如下:最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) 开始已使用TCB链表为空链表优先级TCB为空初始化空闲TCB链表结

38、束任务控制块初始化1、程序流程图 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) static void OS_InitTCBList (void) INT8U i; OS_TCB *ptcb1, *ptcb2; OSTCBList = (OS_TCB *)0; for (i = 0; i (OS_LOWEST_PRIO + 1); i+) OSTCBPrioTbli = (OS_TCB *)0; ptcb1 = &OSTCBTbl0; ptcb2 = &OSTCBTbl1; for (i = 0; i OSTCBNext

39、= ptcb2; ptcb1+; ptcb2+; ptcb1OSTCBNext = (OS_TCB *)0; OSTCBFreeList = &OSTCBTbl0;初始化TCB链表的“头指针”为NULL清空清空OSTCBPrioTbl初始化OSTCBFreeList初始化空TCB链表2、初始化代码 注意:分析源代码最重要的是要关注变量、指针或事件是在何时发生或清除的,往往初学者不注意这些看起来似乎“鸡毛蒜皮”的细节,以至于很难达到“融会贯通”的目的。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务控制块(任务控制块(OS_TCB) 初始化空OS_TCB链表的同时将优先

40、级任务控制块表OSTCBPrioTb1prio全部清空,而变量OSPrioCur与OSPrioHighRdy,OSTCBCur与OSTCBHighRdy都是在OS_InitRdyList()函数中初始化。初始化空OS_TCB链表后的状态最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务就绪表任务就绪表 所谓就绪状态是指任务可以被运行的状态,此时只要CPU有空,就执行处于就绪态的任务,而任务就绪表是用于指示哪些任务处于就绪状态的表格,C/OS-中每个任务的就绪态标志都放在就绪表中。 根据任务的优先级就可以确定该任务在就绪表中的确切位置了。 真正的任务就绪表 索引表,非必须的,为加

41、快处理速度而引入确 定 任 务 在 就 绪 表OSRdyTbl中的所在位,即横坐标X确定变量OSRdyGrp具体数据位,并用来确定就绪表数组元素的下标,即纵坐标YOSRdyTbl虽然被定义为“字节数字节数组组”,但仍被看作是“位数组位数组”,即每一位代表一个任务,其中任何一位为1表示对应的任务处于就绪状态,为0表示对应的任务没有处于就绪状态。就绪表OSRdyTbl数组的大小取决于OS_LOWEST_PRIO的值。if (OSRdyTblprio 3 &= OSMapTblprio & 0 x07) = 0); OSRdyGrp &= OSMapTblprio 3;INT

42、8U const OSMapTbl = 0 x01, 0 x02, 0 x04, 0 x08, 0 x10, 0 x20, 0 x40, 0 x80; OSRdyGrp |= OSMapTblprio 3;OSRdyTblprio 3 |= OSMapTblprio & 0 x07;最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务就绪表任务就绪表 用Y映射出纵坐标位OSRdyTblprio3所有的位都是0OSRdyGrp的相应位才清0预先定义的数组用X映射出横坐标位1、使任务进入就绪态2、使任务脱离就绪态INT8U const OSUnMapTbl = /0, 1,

43、2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x00 to 0 x0F*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x10 to 0 x1F*/ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x20 to 0 x2F*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x30 to 0 x

44、3F*/ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x40 to 0 x4F*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x50 to 0 x5F*/ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x60 to 0 x6F*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x70 to 0 x7F*/ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1

45、, 0, 2, 0, 1, 0,/*0 x80 to 0 x8F*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 x90 to 0 x9F*/ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 xA0 to 0 xAF*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 xB0 to 0 xBF*/ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 xC0 to 0 xCF*/ 4

46、, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 xD0 to 0 xDF*/ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/*0 xE0 to 0 xEF*/ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0/*0 xF0 to 0 xFF*/;y= OSUnMapTblOSRdyGrp;x= OSUnMapTblOSRdyTbly;prio= (y 3) + x;算出最高优先级最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务就绪表

47、任务就绪表 OSRdyGrp为0 x69,以此值为偏移量查表3、查找就绪态优先级最高的任务找出最高优先级任务的横坐标找出最高优先级任务的纵坐标OSRdyTb10为0 x30,以此值为偏移量查表就绪任务组优先级最高的就是4(03+4)最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务就绪表任务就绪表 static void OS_InitRdyList (void) INT16U i; INT8U *prdytbl; OSRdyGrp= 0 x00; prdytbl = &OSRdyTbl0; for (i = 0; i 1,表明发生了中断嵌套。 static void

48、OS_InitMisc (void) OSIntNesting= 0; OSRunning= FALSE;将中断嵌套计数器清零 表示多任务还没有启动1、OS_InitMisc()杂项变量初始化最小内核最小内核 | C/OS-II微小内核分析微小内核分析 OS初始化初始化 当所有用户任务都可能未处于就绪状态的时候,CPU将无事可做。虽然CPU没事可做,但CPU是愚蠢的,必须找一些事给它做,否则程序就会跑飞了。空闲任务就是给CPU找点事做。static void OS_InitTaskIdle (void) #if OS_STK_GROWTH = 1 (void)OSTaskCreate(OS_T

49、askIdle, (void *)0, &OSTaskIdleStkOS_TASK_IDLE_STK_SIZE - 1, OS_IDLE_PRIO); #else (void)OSTaskCreate(OS_TaskIdle, (void *)0, &OSTaskIdleStk0, OS_IDLE_PRIO); #endif堆栈从高地址向低地址生长 堆栈从低地址向高地址生长2、OS_InitTaskIdle()创建空闲任务 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 OS初始化初始化void OS_TaskIdle (void *pdata) pdata =

50、pdata; for (;) 防止编译器发出没有使用pdata的警告 让CPU做一些事情 空闲任务一般是一个空的死循环语句,或是让CPU进入休眠状态,或是不断的给某个变量计数。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务管理任务管理 C/OS-是通过任务控制块来管理任务的,实际上创建任务就是给任务代码分配一个任务控制块。用户任务不可能凭空出 现 , 必 须 由 用 户 使 用 代 码 创 建 , 这 是 通 过 调 用 函 数OSTaskCreate()实现的。 任务创建函数OSTaskCreate()需要4个参数:task: 指向任务代码的指针,即任务函数名,指向任务的

51、代码地址;pdata: 当任务开始执行时传递给任务的参数的指针;ptos: 分配给任务的堆栈的栈顶指针;prio: 分配给任务的优先级。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务管理任务管理 创建任务流程图 1、创建任务流程图 INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) OS_STK *psp; INT8U err; OS_ENTER_CRITICAL(); if (OSTCBPrioTblprio = (OS_TCB *)0); OSTCBPrio

52、Tblprio = (OS_TCB *)1; OS_EXIT_CRITICAL(); psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0); err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); if (err = OS_NO_ERR) if (OSRunning = TRUE); OS_Sched(); else OS_ENTER_CRITICAL(); OSTCBPrioTblprio = (OS_TCB *)0; OS_EXIT_CRITICAL(); return (

53、err); return (OS_PRIO_EXIST); 初始化任务堆栈如果初始化成功,则返回OS_NO_ERR,获得并初始化一个OS_TCB;如果系统中没有OS_TCB可以分配给任务了,则返回OS_NO_MORE_TCB。创建任务成功后,只有调用OSStart()后,OSRunning才始终为TRUE ,才能成功调度任务。表明没有创建成功,使未被成功创建的任务控制块优先级表清零,可以被重新分配任务控制块地址。如果任务创建成功,则返回OS_NO_ERR;如果具有该优先级的任务已经存在,则返回OS_PRIO_EXIST。如果OSTCBPrioTb1prio为空,则说明可以建立任务。为防止其它任

54、务抢占该任务,必须先保留当前任务的优先级,直到任务创建完毕。 进入临界区 退出临界区2、创建任务函数代码 ptos的初始值为ptos&OSTaskIdleStkOS_TASK_IDLE_STK_SIZE1,通过OSTaskStkInit() 任务堆栈初始化后,减去17就是返回值stk所指向的位置, 即psp。pspstk&OSTaskIdleStk(OS_TASK_IDLE_STK_SIZE1)17。 获得任务控制块并初始化最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务管理任务管理 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务管理任务管

55、理 要点:在多任务调度之前,用户必须至少创建一个任务,但任务不能在中断服务程序(ISR)中建立。有时为了节省RAM,对不常用的任务在运行完毕之后采取删除措施,比如由人机界面(如键盘)启动的任务,这是由调用OSTaskDel()函数完成的。 通过分析创建任务OSTaskCreate()函数得知,OSTaskCreate()调用了OSTaskStkInit()任务堆栈初始化函数和OS_TCBInit()函数获得并初始化一个OS_TCB,其相互之间的调用关系如下图。 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务堆栈初始化任务堆栈初始化 栈是限定仅在表尾进行插入与删除操作的线性表

56、,栈的修改是按后进先出的原则进行的。堆栈可以从低地址向高地址生长,也可以从高地址向低地址生长, ARM对于2种方式均支持,但ADS的C编译器仅支持“高地址向低地址生长”的生长方式,并且必须是“满递减堆栈”。满递减堆栈中,栈底指针始终指向栈底的位值,栈顶指针始终指向最后压入的数据,每当插入新的栈顶元素时,栈顶指针减4,删除栈顶元素时,栈顶指针加4。 编写OSTaskStkInit()函数依据的结构OSTaskStkInit()初始化之后的任务堆栈的结构 1、任务堆栈初始化结构 task:任务的开始执行地址,对于C语言来说,就是任务函数名;pdata:当任务开始执行时传递给任务的参数的指针,它应当

57、保存到R0中;ptos:分配给任务的堆栈栈顶指针;otp:保留参数,目前没有使用。 保存禁止中断的次数,这样禁止中断和禁止中断就可以嵌套了。在调用O S _ E N T E R _ C R I T I C A L ( ) 禁 止 中 断 函 数 时 , 它 的 值 加 1 ; 在 调 用OS_EXIT_CRITICAL()时,它的值减1,并且仅在其值为0时才打开中断。每个任务都会有一个OsEnterSum的备份,在任务切换时更新OsEnterSum。以创建OS_TaskIdle空闲任务为例,一共有17个寄存器被压入堆栈,即stk&OSTaskIdleStk(OS_TASK_IDLE_S

58、TK_SIZE1)17。最小内核最小内核 | C/OS-II微小内核分析微小内核分析 任务堆栈初始化任务堆栈初始化 OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) OS_STK *stk; extern void TaskEntry(void); opt = opt; stk = ptos; *stk = (OS_STK) TaskEntry; *-stk = (OS_STK) task; *-stk = 0; . *-stk = 0; *-stk = (unsigned

59、 int) pdata; *-stk = 0 x1f; *-stk = 0; return (stk); 避免编译器警告2、OSTaskStkInt()任务堆栈初始化代码要点:OSTaskStkInit()函数负责建立任务的堆栈,最后返回栈顶指针。有了栈顶指针之后,如果让堆栈指针SP也指向栈顶指针所指向的位置的话,那么在出栈操作恢复现场数据时,可以通过堆栈指针SP得到Task任务开始执行的地址。 R1 R12 获取堆栈指针 PC,TaskEntry执行任务程序 LR 第一个参数使用R0传递 CPSR,允许 IRQ, FIQ 中断 禁止中断计数器OsEnterSumstk&OSTaskI

60、dleStk(OS_TASK_IDLE_STK_SIZE1)17最小内核最小内核 | C/OS-II微小内核分析微小内核分析 获得并初始化一个获得并初始化一个OS_TCB 任务控制块函数OS_TCBInit()是在创建任务时初始化的,这个函数首先为被创建任务从空任务控制块链表获取一个任务控制块,然后对任务控制块的各个成员进行赋值,最后再将这个任务控制块链接到任务控制块链表的头部。获得并初始化一个OS_TCB流程图 最小内核最小内核 | C/OS-II微小内核分析微小内核分析 获得并初始化一个获得并初始化一个OS_TCB INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, I

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论