ucosII任务切换是怎样实现的
- 格式:pdf
- 大小:323.28 KB
- 文档页数:4
uCOS-II的任务切换机理及中断调度优化uC/OS-II的任务切换机理及中断调度优化摘要:μC/OS-II是一种适用于嵌入式系统的抢占式实时多任务操作系统,开放源代码,便于学习和使用。
介绍μC/OS-II在任务级和中断级的任务切换原理,以及这一操作系统基于嵌入式系统的对于中断的处理;相对于内存资源较少的单片机,着重讨论一种优化的实用堆栈格式和切换形式,以提高资源的利用率;结合MSP430单片机,做具体的分析。
关键词:实时多任务操作系统μC/OS MSP430 中断堆栈引言在嵌入式操作系统领域,由Jean J. Labrosse开发的μC/OS,由于开放源代码和强大而稳定的功能,曾经一度在嵌入式系统领域引起强烈反响。
而其本人也早已成为了嵌入式系统会议(美国)的顾问委员会的成员。
不管是对于初学者,还是有经验的工程师,μC/OS开放源代码的方式使其不但知其然,还知其所以然。
通过对于系统内部结构的深入了解,能更加方便地进行开发和调试;并且在这种条件下,完全可以按照设计要求进行合理的裁减、扩充、配置和移植。
通常,购买RTOS 往往需要一大笔资金,使得一般的学习者望而却步;而μC/OS对于学校研究完全免费,只有在应用于盈利项目时才需要支付少量的版权费,特别适合一般使用者的学习、研究和开发。
自1992第1版问世以来,已有成千上万的开发者把它成功地应用于各种系统,安全性和稳定性已经得到认证,现已经通过美国FAA认证。
1 μC/OS-II的几大组成部分μC/OS-II可以大致分成核心、任务处理、时间处理、任务同步与通信,CPU的移植等5个部分。
核心部分(OSCore.c) 是操作系统的处理核心,包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。
能够维持系统基本工作的部分都在这里。
任务处理部分(OSTask.c) 任务处理部分中的内容都是与任务的操作密切相关的。
包括任务的建立、删除、挂起、恢复等等。
uCOS-II中的任务切换机制【@.1 函数周期与死循环】一般函数的生命周期很简单,从开始调用函数起,直到函数返回,即结束。
这样一来就完成了这个函数的使命,它也就不再需要了。
对于一般的函数就是这样,但是回过头想想,对于一个系统、OS、或者工业控制中的一个控制器重的系统个,函数返回是很轻易很随便的就能返回吗?返回就意味着函数结束,死亡,若是想系统这样一个很大的函数,它的返回就意味着系统结束。
因此,对于系统的函数返回有些时候我们不希望它返回,返回时是需要好好设计的,像嵌入式中的控制程序我们也并不需要它返回,直接关机就好了。
因此,一个系统往往就是一个很大的循环,不停的扫描,而我们编程的时候对于这个死循环是需要好好设计的。
考虑以下一个控制要求,@.按键控制电机启、停、正转反转,并每秒发送CAN报文报告当前情况。
我们可以有多种方法实现这一要求:方法一:每次在循环体重扫描当前按键的电平,从而进入对应的控制电机函数,如果所有电平都没有信号则直接进入下一个循环。
发送CAN报文就直接用一个定时中断。
这样的好处就是编程简单直白,每次循环进入不同的电机控制函数,坏处很明显,一定要等待到下一个循环才能进入其他的电机控制函数,每次循环的时间不好控制,不管你用函数指针还是if/else来判断,每次循环一定要等待电机动作结束才能进入下一个循环。
方法二:改用外部中断来处理按键。
仅当按键按下时触发外部中断,从而控制响应的电机进行操作。
这样的好处就是循环体简单,可以仅仅就是一个计数器加一,所有控制都等中断来实现。
但这样带来的问题也很明显,就是中断嵌套问题。
比如当电机正转时按下停止按钮,这时由于是在中断中,停止按钮是否真的能够得到响应?这就涉及到中断嵌套问题,并不见得所有CPU都能支持中断嵌套,我的这一篇文章对中断嵌套问题进行了一个讨论。
方法三:采用RTOS的思想,加入任务调度系统。
每次任务调度系统就是一个小小的循环,对于各个任务进行轮询,当这次轮询发现某任务是已经就绪的优先级最高的任务,则交给CPU处理,所有中断与任务,任务与任务之间有通讯机制可以交换信息。
UC/OS II 多任务机制前面已经说过,uC/OS-II是一种基于优先级的可抢先的多任务内核。
那么,它的多任务机制到底如何实现的呢?了解这些原理,可以帮助我们写出更加健壮的代码来。
首先我们来看看为什么多任务机制可以实现?其实在单一CPU的情况下,是不存在真正的多任务机制的,存在的只有不同的任务轮流使用CPU,所以本质上还是单任务的。
但由于CPU执行速度非常快,加上任务切换十分频繁并且切换的很快,所以我们感觉好像有很多任务同时在运行一样。
这就是所谓的多任务机制。
由上面的描述,不难发现,要实现多任务机制,那么目标CPU必须具备一种在运行期更改PC的途径,否则无法做到切换。
不幸的使,直接设置PC指针,目前还没有哪个CPU支持这样的指令。
但是一般CPU都允许通过类似JMP,CALL这样的指令来间接的修改PC。
我们的多任务机制的实现也正是基于这个出发点。
事实上,我们使用CALL指令或者软中断指令来修改PC,主要是软中断。
回想一下你在微机原理课程上学过的知识,当发生中断的时候,CPU保存当前的PC和状态寄存器的值到堆栈里,然后将PC设置为中断服务程序的入口地址,再下来一个机器周期,就可以去执行中断服务程序了。
执行完毕之后,一般都是执行一条RETI指令,这条指令会把当前堆栈里的值弹出恢复到状态寄存器和PC里。
这样,系统就会回到中断以前的地方继续执行了。
那么设想一下?如果再中断的时候,人为的更改了堆栈里的值,那会发生什么?或者通过更改当前堆栈指针的值,又会发生什么呢?如果更改是随意的,那么结果是无法预料的错误。
因为我们无法确定机器下一条会执行些什么指令,但是如果更。
关于uCOS-Ⅱ中任务切换的仪器仪表的实现实例假设某仪器要实现A/D采样、输入控制、显示等功能。
uC/OS-Ⅱ*作系统将对这三个任务进行管理,协调各自工作。
任务描述:统计任务:每做完例行统计后执行一次显示输出;初始任务:每隔10节拍进行一次A/D采样;用户任务:每秒判断一次是否有输入控制信号。
实验中为了测量与观察方便,采用对89C51的P1口每次取反的*作来模拟仪器执行的任务,由于关心的是*作系统的*能,而非任务本身,因此简化是可行的。
设置时钟节拍:50Hz三个任务(包含系统空闲任务):任务名优先级*作空闲任务63空*作统计任务62每秒做一次统计并对P1。
2取反初始任务4每秒对P1。
0取反一次用户任务5每10个节拍对P1。
1取反一次设计中须注意以下两点:(1)任务的现场保护:除PUSH到堆栈的CPU寄存器外,还应包括任务运行过程中保存到堆栈的信息,所以每次保存的信息量是变化的。
(2)任务堆栈的大小应根据实际情况充分估计,避免过大或者不足。
在这个例子中定义了TASKSTKSIZE(任务堆栈)为64个字节,故在内部RAM中预留出64个字节作为*作系统的系统堆栈,用全局变量定义如下:INT8Uidatasp[64]at0x30;该语句表示系统堆栈从30H开始。
(1)开始多任务调度每个任务堆栈初始化情况:堆栈指针SP=48-1+8+5+2+2+sizeof(void*),即系统堆栈放入以上信息后SP指针所指的位置。
下面开始多任务调度:系统堆栈指针SP初始值30H,将初始任务的任务堆栈内容(在xdata段)Load到系统堆栈内,完成后SP应指向系统堆栈栈顶,然后运用pop指令切换到初始任务。
(2)在任务运行中进行任务切换切换前系统堆栈为:将系统堆栈中从当前SP开始一直到30H的堆栈信息保存到当前任务堆栈中,然后把新任务的任务堆栈的内容Load到系统堆栈中来。
然后用POP指令将任务切换到新任务中去。
(3)在中断返回时切换任务须保存的信息在中断时应保存,切换任务时,只要将SP减去由于调用OSIntExit()和OSIntCtxSw()两个函数而使SP指针增加的4个字节,其他与任务级切换一样。
μC\OS-Ⅱ操作系统的任务切换作者:司新生来源:《数字技术与应用》2010年第06期摘要:μC\OS-Ⅱ操作系统是一个多任务占先式实时操作系统,每一个任务由三部分组成,任务控制块,任务的私有堆栈、任务代码。
每一个任务有一个决定其重要性的任务优先级,系统通过任务就绪表来进行任务的切换,就绪的任务在任务就绪表中设置其标志位,退出就绪的任务在就绪表中撤消其标志位。
任务的切换过程就是通过任务就绪表找到优先级最高的任务,保存原来运行任务的上下文到该任务的私有堆栈中,从最高优先级的任务私有堆栈中复制断点数据到工作寄存器中,pc指针指向该任务的代码段,实现了任务的切换。
关键词:操作系统任务控制块优先级任务切换1 μC\OS-Ⅱ的任务μC\OS-Ⅱ操作系统是一个多任务系统,它最多可以管理64个任务,但两个优先级最低的任务已被系统占用,一个是统计任务,一个是空闲任务。
空闲任务的作用为当操作系统没有其它任务执行时,就转入空闲任务而不使系统没事可做。
任务的结构每一个任务都有如下结构。
它由任务控制块TCB,任务代码,任务堆栈组成,多个任务控制块形成一个任务控制块链表。
每一个任务在创建时都被分配有一个任务优先级,优先级序号从0到63,优先级数值越大则表示优先级越低,最高的优先级是优先级序号为0的任务,最低的优先级是优先级为63的任务。
操作系统可以设定管理的任务数,在OS_CFG.H文件中,可以定义OS_LOWEST_PRIO值,该值最大为63。
每一个任务都有唯一的任务优先级,μC\OS-Ⅱ操作系统任务切换的关键就是该任务的优先级,操作系统总是运行处于就绪状态的最高优先级的任务。
创建任务主要完成四项任务,一是指出任务代码存放的地址,二是指明任务参数指针,也即任务参数地址,三是指明任务堆栈栈顶指针,在进行任务切换时保存或恢复与任务相关的寄存器的值,四是确定任务的优先级,优先级的高低决定了任务的紧迫性和重要性。
任务创建的代码如下。
uC/OS-II的运行机制在嵌入式系统的应用中,实时性是一个重要的指标,而优先级翻转是影响系统实时性的重要问题。
本文着重分析优先级翻转问题的产生和影响,以及在uC/OS-II 中的解决方案。
uC/OS-II 采用基于固定优先级的占先式调度方式,是一个实时、多任务的操作系统。
系统中的每个任务具有一个任务控制快OS_TCB,任务控制块记录任务执行的环境,包括任务的优先级,任务的堆栈指针,任务的相关事件控制块指针等。
内核将系统中处于就绪态的任务在就绪表(ready list)进行标注,通过就绪表中的两个变量OSRdyGrp 和OSRdyTbl[]可快速查找系统中就绪的任务。
在uC/OS-II 中每个任务有唯一的优先级,因此任务的优先级也是任务的唯一编号(ID),可以作为任务的唯一标识。
内核可用控制块优先级表OSTCBPrioTbl[] 由任务的优先级查到任务控制块的地址。
uC/OS-II 主要就是利用任务控制快OS_TCB、就绪表(ready list)和控制块优先级表OSTCBPrioTbl[]来进行任务调度的。
任务调度程序OSSched()首先由就绪表(ready list)中找到当前系统中处于就绪态的优先级最高的任务,然后根据其优先级由控制块优先级表OSTCBPrioTbl[] 取得相应任务控制块的地址,由OS_TASK_SW()程序进行运行环境的切换。
将当前运行环境切换成该任务的运行环境,则该任务由就绪态转为运行态。
当这个任务运行完毕或因其它原因挂起时,任务调度程序OSSched()再次到就绪表(ready list)中寻找当前系统中处于就绪态中优先级最高的任务,转而执行该任务,如此完成任务调度。
若在任务运行时发生中断,则转向执行中断程序,执行完毕后不是简单的返回中断调用处,而是由OSIntExit()程序进行任务调度,执行当前系统中优先级最高的就绪态任务。
当系统中所有任务都执行完毕时,。
班级学号姓名同组人实验日期室温大气压成绩实验二 UCOS-II任务管理一、实验目的1、掌握UCOS-II中任务管理的函数的应用。
2、掌握UCOS-II在STM32平台下对硬件的控制。
3、掌握开发UCOS-II应用的程序结构。
二、实验步骤1、UCOSII工作原理UCOSII提供系统时钟节拍,实现任务切换和任务延时等功能。
这个时钟节拍由OS_TICKS_PER_SEC(在os_cfg.h中定义)设置,一般我们设置UCOSII的系统时钟节拍为1ms~100ms。
本次实验利用STM32的SYSTICK定时器来提供UCOSII时钟节拍。
UCOSII的任何任务都是通过一个叫任务控制块(TCB)的东西来控制的,每个任务管理块有3个最重要的参数:(1)任务函数指针;(2)任务堆栈指针;(3)任务优先级。
在UCOSII中,使用CPU的时候,优先级高(数值小)的任务比优先级低的任务具有优先使用权,即任务就绪表中总是优先级最高的任务获得CPU使用权,只有高优先级的任务让出CPU使用权(比如延时)时,低优先级的任务才能获得CPU使用权。
UCOSII不支持多个任务优先级相同,也就是每个任务的优先级必须不一样。
任务的调度其实就是CPU 运行环境的切换,即:PC指针、SP指针和寄存器组等内容的存取过程UCOSII的每个任务都是一个死循环。
每个任务都处在以下5种状态之一的状态下,这5种状态是:睡眠状态、就绪状态、运行状态、等待状态(等待某一事件发生)和中断服务状态。
睡眠状态,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态。
就绪状态,系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,任务已经准备好了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行,这时任务的状态叫做就绪状态。
运行状态,该任务获得CPU使用权,并正在运行中,此时的任务状态叫做运行状态等待状态,正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给别的任务而使任务进入等待状态。
uCOS-II任务调度过程ucos-II是基于任务优先级抢占式任务调度法的,就是内核在管理调度时,调用任务切换函数(一般为SSched()),在该函数中将此时已处于就绪状态(条件一)并且为最高优先级(条件二)的任务的保存于其栈中的相应信息压入cpu寄存器中(软中断完成),然后cpu 开始运行该任务的代码。
内核是何时进行任务调度的呢?虽然uC/OS-II是可被剥夺资源的内核(高优先级可强行占有低优先级正在使用的资源),但此事发生的前提是内核实时"检测"到了更高就绪的优先级了,那么内核是怎样来实时检测的呢?带着这个问题让我们再来看看任务的结构——里边有函数OSTimeDly(OS_TICKS_PER_SEC),一看就知道这是个延时函数,除了延时外它还会有其他用途呢?经查看其源码了解到里边有一条代码:OSSched(),对,函数OSTimeDly()的作用就是将此时正在运行的函数挂起(保存任务控制块OS_TCB中的相应信息)(任务控制块OS_TCB是系统分配给每个任务的信息存储单元),然后调用函数OSSched()进行任务切换,进而执行就绪的最高优先级任务。
此刻,我们了解到uCOS-II的任务切换是在执行的任务中调用延时函数OSTimeDly()进行的。
现在,还有一个问题还没解决,就是当延时到了,内核如何将资源返还给被延时挂起的任务?我们先来了解一下任务控制块(OS_TCB),任务控制块是一个数据结构,当任务的cpu使用权被剥夺时,uC/OS-II用它来保存该任务的状态。
当任务重新得到cpu使用权时,任务控制块确保任务从当时被中断的那一点丝毫不差地继续执行。
OS_TCB全部驻留在RAM中。
在OS_TCB中有一项时间延时项OSTCBDly,调用函数OSTimeDly()过程中有一步骤就是给OSTCBDly赋延时值。
uC/OS—II中有函数OSTimTick(),叫时钟节拍函数,它的一项工作就是给每个用户任务控制块OS_TCB中的时间延迟项OSTCBDly减1(如果该项不为零),当某项任务的任务控制块中的时间延时项OSTCBDly减为0时,这个任务就进入了就绪态,等待任务切换。
UC/OS-II学习笔记之——任务切换是怎样实现的问题是,
o Uc/OS-II如何切换任务?通过任务调度器OS_Sched(),那么谁在调用这个函数?
o CPU在这里肯定有作用,因为任务切换必然涉及到CPU寄存器的入栈和出栈,
那么这一块工作是如何完成的?
书上讲:为了做到任务切换,运行OS_TASK_SW(),人为模仿一次中断。
中断服务子程序或陷阱处理(trap hardler),也称作事故处理(exception handler),必须给汇编语言函数OSCtxSw()提供中断向量[1.92]。
那么,“人为模仿一次中断”是什么意思?
是指:OS_TASK_SW()触发了一个中断,由中断完成了任务切换?
阅读源代码,查找答案……
#define OS_TASK_SW()OSCtxSw()//这是一个宏调用,定义在os_cpu.h,Os_cpu_a.asm中定义了OSCtxSw
NVIC_INT_CTRL EQU0xE000ED04
NVIC_PENDSVSET EQU0x10000000
;******************************************************************************
;PERFORM A CONTEXT SWITCH(From task level)
;void OSCtxSw(void)
;
;Note(s):1)OSCtxSw()is called when OS wants to perform a task context switch.This function
;triggers the PendSV exception which is where the real work is done.
;******************************************************************************
OSCtxSw
LDR R0,=NVIC_INT_CTRL;Trigger the PendSV exception(causes context switch) LDR R1,=NVIC_PENDSVSET
STR R1,[R0]
BX LR
注释说明该段汇编代码触发了一个PendSV的异常。
在《cortex-M3权威指南》中搜索关键词“PendSV”,可悬挂请求pend able request,在《2.9中断和异常》表7.1“系统异常清单”所列举的异常类型中,定义PendSV为“为系统设备而设的可悬挂请求”[2.109]
(中断优先级PendSV>SysTick>外部中断)
那么,这个PendSV异常的作用到底是什么?
PendSV典型使用场合是上下文切换(任务间切换)。
上下文被触发的条件是:[2.125] o系统滴答定时器(SYSTICK)中断(轮转调度中需要)
o执行一个系统调用(TASK level code)
个中事件的流水账记录如下:
(1)任务A呼叫SVC来请求任务切换(例如,等待某些工作完成)
(2)OS接收到请求,做好上下文切换的准备,并且悬起一个PendSV异常。
(3)当CPU退出SVC后,它立即进入PendSV,从而执行上下文切换。
(4)当PendSV执行完毕后,将返回到任务B,同时进入线程模式。
(5)发生了一个中断,并且中断服务程序开始执行
(6)在ISR执行过程中,发生SysTick异常,并且抢占了该ISR。
(7)OS执行必要的操作,然后悬起PendSV异常以作好上下文切换的准备。
(当SysTick退出后,回到先前被抢占的ISR中,ISR继续执行
(9)ISR执行完毕并退出后,PendSV服务例程开始执行,并且在里面执行上下文切换
(10)当PendSV执行完毕后,回到任务A,同时系统再次进入线程模式
中断正在执行,禁止上下文切换。
否则中断被延时,而延时时间不可预知——这是实时系统不能容忍的。
早期解决办法,无中断执行时,才执行上下文切换(切换期间无法响应中断)。
弊端在于任务切换可能被拖延的很久。
[2.126]
PendSv可以解决这个问题,OS会悬起一个PendSv,PendSv异常处理函数处理上下文切换请求——
(1).如果还有未处理完的ISR(图7.17后半段SysTick触发了上下文切换),PendSv被悬起,延迟上下文切换请求,直到处理完ISR后才执行上下文切换;
(2).如果没有任何ISR(图7.17前半段),则立刻调用PendSV异常处理函数完成上下文切换。
需要把PendSv编程为最低优先级的异常。
也就是说,PendSv可以像普通中断一样被悬起,OS可以利用它缓期执行一个异常(PendSV)——在其它重要任务完成之后才执行动作(上下文切换)。
悬挂PendSv的方法是:往NVIC的PendSv悬起寄存器中写1。
悬起后,如果优先级不够高,则将缓期执行。
ICSR(Interrupt Control and State Register0xE000ED04)[bit28]
具体的程序调试:
执行系统函数(悬起PendSV异常的手段之一)OSTimeDlyHMSM(),调用OSTimeDly(),调用task scheduler OS_Sched(),终于执行这个宏调用,调用执行OSCtxSw(),(go to definition of OSCtxSw却不能跳转到汇编代码!),汇编代码把1写入中断控制及状态寄存器寄存器ICSR[bit28]从而悬起一个PendSV异常,这是上图7.17中的步骤2,
Void OS_Sched(void)//任务调度器
{
关中断;
if(没有中断&&没有关调度器)
OS_TASK_SW();//在这里悬起PendSV异常,开中断前先在PendSV Handler设置断点开中断;
}
执行完开中断语句后,程序立刻跳到PendSV异常处理函数PendSV Handler(在此处),这个跳转是如何完成的?具体的入栈、出栈涉及到CPU的哪些寄存器,操作是如何完成的?
执行完PendSV Handler后跳转到空闲任务,实现了任务切换:。