通过不断地学习和实验UC/OS-II,终于在五一前在ARM平台上实现了多任务的创建、调度运行、挂起,任务间信号量、互斥型信号量、邮箱、消息队列和事件标识组方式下的通信机制,接下来则继续学习UC/OS-II的移植方法,在此趁五一假期之际,我将UC/OS-II的特点用自己通俗的语言介绍一下,希望能让大家在没接触UC/OS-II的情况下大概明白UC/OS-II的用处和特点,共同学习和进步,为学习UC/OS-II的同学提供有用的资料。另外,如有错误和不当之处,还请指正和发表您的心得体会。
谢谢!
一.有关UC/OS-II实时系统
1)UC/OS-II的名称
嵌入式实时操作系统UC/OS-II中文意为“微控制器操作系统版本2”,
它是一个完整的、可移植、固化、裁剪的占先式可剥夺型实时多任务内核。
2)UC/OS-II的用处
UC/OS-II多任务管理意在使多个需要处理器处理的事务在同时间段内同
步完成,通过充分利用处理器的处理资源,使处理器的处理效率到达更大
限度,UC/OS-II最多可实现同步管理64个任务
3)UC/OS-II多任务管理思想
用日常的例子打比方,比如处理器要完成一段程序就好比我们将做一顿饭,传统的方法是流水线式(单线程),如图Pic1.1:
Pic1.1
可见总用时位各过程的总和,我们可以想象得出这样的做法是很浪费时间的,我们任何人都不会这么去做,采用UC/OS-II多任务管理思想则可以将此事务按照另一种方式完成,如图Pic1.2中的多线程方式,由图比较可以看出,整个事务在t5时刻完成,当菜炒好时饭也做好不久,这样的方法使总用时减少了,分析原因,可以知道是我们在等饭做好的时候没有光等,而是开始洗菜,炒菜去了。同样的方式下,如果在等任何一件事务完成的过程中去做其他事情我们的做事效率是不是更快了呢,答案当然是肯定的。UC/OS-II支持最多同时64个任务的管理。
Pic1.2
二.UC/OS-II实时系统中有关概念介绍
1)任务 也叫线程,是一个简单的无限循环的程序,它是由一个大的
事务分割成多个小事务块后得来的,这些小事务块在时间上不冲 突,即可以同时进行,而在运行所有小事务块过程中,依然和运行 完整大事务的效果相同。UC/OS-II中每个任务有自己的特定信息块 (TCB)和独有的优先级级别,处理器通过这些信息管理这些任务, 在多个任务上调度运行、挂起任务等操作。
需要注意的是因为处理器在管理多任务过程中,任何时刻都有
且只能有一个任务在运行,故其它任务就出现了不同的状态,在
UC/OS-II中任务有五种状态,如下:
1.睡眠状态任务 2.就绪状态任务
3.运行状态任务 4.等待状态任务 5.被中断状态任务
任务还可以按图Pic1.2理解,其中的任务就包括
Task_1: 洗米 Task_2 :烧饭 Task_3: 洗菜 Task_4: 炒菜
状态 |
t0~t1 |
t1~t2 |
t2~t3 |
t3~t4 |
t4~t5 |
Task_1 |
运行态 |
·睡眠态 |
·睡眠态 |
·睡眠态 |
·睡眠态 |
Task_2 |
·睡眠态 |
运行态 |
等待态 |
等待态 |
·睡眠态 |
Task_3 |
·睡眠态 |
·睡眠态 |
运行态 |
·睡眠态 |
·睡眠态 |
Task_4 |
·睡眠态 |
·睡眠态 |
·睡眠态 |
运行态 |
运行态 |
表中加“·”符号的表示任务最可能处于的状态
2)调度 (schedulers),它是内核的主要职责,就是决定该轮到那个任
务运行了,UC/OS-II依据任务的优先级别执行调度工作
任务的调度(切换)过程
因为uc/os-ii总是运行进入就绪状态的最高优先级的任务。所以, 确定哪个任务优先级最高,下面该哪个任务运行,这个工作就是由 调度器(scheduler)来完成的。
UC/OS-II中有产生调度有两种方式,任务级的调度和中断级调度。任务级的调度是由函数OSSched()完成的,而中断级的调度是由函数OSIntExt()完成。对于OSSched(),它内部调用的是OS_TASK_SW()完成实际的调度(人为模仿一次中断);OSIntExt()内部调用的是OSCtxSw()实现调度。
任务切换其实很简单,由如下2步完成:(1)将被挂起任务的处 理器寄存器推入自己的任务堆栈。(2)然后将进入就绪状态的最高 优先级的任务的寄存器值从堆栈中恢复到寄存器中。
图Pic1.2中t2时刻即产生任务级调度过程
3)任务控制块(OS_TBC) 任务状态存放于OS-TBC数据结构中;保存了任 务的状态、优先级、断点数据等,当任务得到CPU使用权后,任务 控制块能确保任务能从被中断点那一点丝毫不差地继续执行。
每个任务都有自己的OS_TBC控制块,OS_TBC的数量由 OS_MAX_TASK决定,任务数量少,当然OS_TBC占用RAM的空 间就少。
任务一旦建立,任务控制块OS_TBC将被赋值。
所有的任务控制块OS_TBC都是放在任务控制块列表数组 OSTCBTbl[]中。在ucos-ii初始化时,所有任务控制块OS_TBC都被链表连接成单向空任务链表。
注意:UC/OS-II每创建一个任务时就在RAM中建立一个相应的OS_TBC
UC/OS-II学后总结(二)
三.UC/OS-II要点记录
1.中断服务所做的事应尽量少做,应把大部分工作留给任务去做。
2.OSIntExit()和OSSched()有点相似,但OSIntExit()使中断nestiing减1,而重新调度的条件是:中断nesting和锁定nesting计算器(OSLockNesting)均为0。
3.OS_TASK_SW()和OSCtxSw()的区别:后者使用于中断服务程序中,中断返回已经对cpu做了保存工作。而前者需要采用模仿软中断返回指令实现cpu保存。
4.时钟节拍
(1)UC/OS-II需要提供周期性的信号源,用于实现时间延迟和确认超时。 节拍率应为10~100Hz,时钟节拍率越高,系统额外的负荷就越重。
(2)系统多任务启动时候(调用OSStart())之后,第一件初始化事情就是 初始化定时中断。
(3)UC/OS-II中的时钟节拍服务是通过在中断服务子程序(OSTickISR()) 中调用OSTimeTick()实现的。OSTimeTick()跟踪所以任务的定时器和超 时时限。 时钟节拍中断服从一般中断规则(含任务调度判断)。
5.OSTCBList指向任务控制块链表的开始,而且它总是指向最新建立的任务。
6.非常好的一个图示:OSInt()之后的变量和数据结构。请参阅P112
7.启动多任务时,执行OSStart()函数,OSStart()先从任务就绪表中找出用户建立的优先级最高的任务的任务控制块,而后调用了OSStartHighRdy(),此函数实质上是将任务堆栈中的保存内容返回到cpu寄存器中,然后执行一条中断返回指令。
【信号量专题】
1. 信号量
(1)uc/os-ii的信号量是由两个部分组成:一部分是16位的无符号整型信号量的计数值(0~65535);另一部分是等待该信号量的任务组成的等待任务表。(另外参考事件控制块ECB)
(2)信号量可以是2值的变量(称为二值信号量),也可以是计数式的。根据信号量的值,内核跟踪那些等待信号量的任务。
(3)建立信号量的工作必须在任务级代码中或者多任务启动之前完成。
(4)任务要得到信号量的问题。
想得到信号量的任务,必须执行等待操作(pend)。如果信号量有效(非0),则信号量减1,任务得以继续运行。如果信号量无效,则等待信号量的任务就被列入等待信号量的任务表中。多少内核允许定义等待超时,当等待时间超过了设定值,该信号量还是无效,则等待该信号量的任务进入就绪态,准备运行,并返回出错代码(等待超时错误)。
(5)任务对信号量的释放问题。
任务执行发信号(post)操作来释放信号量。如果没有任务等待信号量,那么信号量的值仅是简单的加1(则信号量大于0,有效);如果有任务等待该信号量,那么就会有另一个任务进入就绪态,信号量的值就不加1。
之后,这个释放的信号量给那个等待中的任务,要看内核如何调度的。收到信号量的任务可能如下:
◆等待任务中,优先级最高的;(uc/os-ii仅支持这种方式)。
◆等待任务中所有等待该信号量的任务
2. 信号量的有效与无效问题
信号量有效:信号量的计算器非0(.OSEventCnt!=0)。信号量有效表示任务对 资源可用。
信号量无效:信号量的计算器为0。信号量无效表示任务对目前资源不可用,需 要等待其他另一个任务(或者中断服务子程序)发出该信号量
3. 信号量的值(.OSEventCnt)大小表示什么?
①二值信号量,表示任务可以独占共享资源。
②计数式信号量,用于某资源可同时为N个任务所用。
4. 信号量有关的三个重要函数分析
◆OSSemCreate() 创建一个信号量 (注:由任务或启动代码操作)
创建工作必须在任务级代码中或者多任务启动之前完成。功能只要是先获取一个事件控制块ECB,写入一些参数。其中调用了OS_EeventWaitListInt()函数,对事件控制块的等待任务列表进行初始化。完成初始化工作后,返回一个该信号量的句柄(Handle)。
◆OSSemPend() 等待一个信号量 (注:只能由任务操作)
本函数应用于任务试图获得共享资源的使用权、任务需要与其他任务或中断同步及任务需要等待特定事件发生的场合。
如果任务Task_A调用OSSemPend(),且信号量的值有效(非0),那么OSSemPend()递减信号量计数器(.OSEventCnt),并返回该值。换句话说,Task_A获取到共享资源的使用权了,之后就执行该资源。
如果如果任务Task_A调用OSSemPend(),信号量无效(为0),那么OSSemPend()调用OS_EventTaskWait()函数,把Task_A放入等待列表中。(等待到什么时候呢?要看OSSemPost()(或者等待超时情况),由它释放信号量并检查任务执行权,见下资料)
◆OSSemPost() 发出(释放)一个信号量 (注:由任务或中断操作)
本函数其中调用OS_EventTaskRdy()函数,把优先级最高的任务Task_A(在这假如是Task_A,另外假设当前调用OSSemPost()的任务是Task_B)从等待任务列表中去除,并使它进入就绪态。然后调用OSSched()进行任务调度。如果Task_A是当前就绪态中优先级最高的任务,则内核执行Task_A;否则,OSSched()直接返回,Task_B继续执行。
【互斥型信号量专题】
1.互斥型信号量(mutex)
互斥型信号量具备uc/os-ii信号量的所有机制,但还具有其他一些特性。
任务可利用互斥型信号量来实现对共享资源的独占处理。
Mutex是二值信号量,1表示资源是可以使用的。
2.关于优先级反转(PIP)
下面概述优先级反转原理:
假设有三个任务,分别命名为A,B,C;A的优先级最高,C的优先级最低。任务A和任务B处于挂起状态(请注意这条件),等待某一事件的发生,任务C正在运行。当任务C等待到共享资源(命名为S1)并使用后,如果任务A等待得事件到来之后,由于A的优先级最高,所以就会剥夺任务C的CPU使用权。运行过程中,任务A也要使用资源S1,但S1的信号量还被任务C占用着,所有任务A只能进入挂起状态,等待任务C对S1的信号量的释放。此时任务C得以继续运行。
同理,任务B的事件到来后,会剥夺任务C的CPU使用权。任务B把事情搞定以后,把CPU使用权归还给任务B。任务B又得以继续运行,任务B认真处理完毕资源S1后,终于可以释放S1的信号量。而处于等待该信号量的任务A马上得到信号量并开始处理共享资源S1。
综述上面情况,任务C和任务A的优先级发生了反转。而互斥型信号量就是具有解决优先级反转问题的特性。
3.UC/OS-II的互斥型信号量由三个部分组成:
◆一个标志,指示mutex是否可以使用(0或1)
◆一个优先级,准备一旦高优先级的任务需要这个mutex,赋予给占有mutex的任务。
◆一个等待该mutex的任务列表。