首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

UC0S-II总览更新

UC0S-II总览更新

本帖最后由 look_w 于 2017-9-23 15:49 编辑

3.5 临界区 μC/OS-II还有一个临界的概念,所谓临界区,就是一段特殊的代码。在这段代码内不允许中断的响应,以此来保证这段代码的原子性。临界代码段通过调用开关中断两个宏来实现的。
3.6 μC/OS-II任务的管理3.6.1 对就绪任务的管理μC/OS-II定义了一个就绪表的数据结构,跟普通的数组非常像(也就是一维数组),但是被赋予了特殊的意义。就绪表中每一位表示一个优先级的任务是否处于就绪状态。而每一位的下标则表示任务的优先级。通过特殊的数据结构和意义,就绪任务的管理效率很高。

任务就绪表是由一个OSRdyTbl数组表示,数组大小(OS_RDY_TBL_SIZE)由最低优先级(OS_LOWEST_PRIO)确定, 这样可以减少不必要的空间浪费,节约RAM资源。OSRdyTbl[]是INT8U 类型数组,每一个元素占8位。每一位表示一个优先级状态(1为就绪,0则未就绪)。8个元素则可以表示64个优先级(8*8=64)。为加速就续表的查找,Labrosse把每个OSRdyTbl元素划为每一优先级组,8个元素则有8个优先级组,它定义了一个INT8U类型的8位变量OSRdyGrp ,OSRdyGrp的每一位对应每个优先级组。如下图:
任务优先级的第三位用于确定任务在osRdyTb1中在元素的第几位,接着的三位用于指定是第几个元素。
假设优先级31的任务第一个加入了就绪任务表,此时OSRdyGrp和OSRdyTbl的情况:
OSRdyGrp的第3位为1,表示第3优先级组有就绪任务。OSRdyTbl的第7位为1,表示第31优先级的任务被就绪。
用此可以使任务加入或脱离就绪表。调度的时候即是查找此表,找出最高的优先级,从而找到任务控制块,执行该任务。
3.6.2 任务的创建、挂起和其他操作 μC/OS-II提供了两个函数可以创建任务,它们是OSTaskCreate()和OSTaskCreateExt(),任务在创建之后也可以挂起或者恢复,这同样要使用μC/OS-II提供的系统函数。挂起任务使用函数OSTsakSuspend(),恢复被挂起的任务使用函数OSTaskResume()。μC/OS-II还提供了任务的删除,优先级的修改,查询任务信息等其他功能的函数。

3.6.3 任务的调度 μC/OS-II任务的调度是由调度器完成的。所谓调度器实际上是一个函数OSShed();此函数通过搜索任务就绪表来获得最高优先级的就绪任务,在由该任务的优先级来获得任务的控制块再来实现任务的切换。

           任务的调度不是任何时刻都进行的,而是有时机的。μC/OS-II任务当有以下情况发生时将产生一次任务调度:
           ● 创建了新任务,并在就绪表中进行了登记
           ● 有任务被删除
           ● 有处于等待的任务被唤醒
           ● 中断退出的时候
           ● 正在运行的任务等待某事件而进入等待状态
           ● 正在运行的任务自愿放弃微处理器占有权而等待一段时间

3.6.4任务的初始化和启动 μC/OS-II中定义了大量的全局变量和数据结构。在μC/OS-II运行以前需要对这些全局变量和数据结构进行初始化。为了完成μC/OS-II的初始化,系统提供了初始化函数OSInit()。μC/OS-II的启动也是通过系统提供的函数OSStart()来实现的。OSStart()在判断系统没有在运行后来获得就绪表中最高优先级的就绪任务,并调用函数OSStartHighRdy()来启动系统。

3.6.5 中断和时钟 实时系统为了能够响应异步事件,通常会采用中断。μC/OS-II也采用了中断来响应外部事件。μC/OS-II处理中断过程大致如下:当系统开中断时,系统接收到中断然后找到中断服务程序的入口地址执行中断,执行完成后退出中断。这里要提到的一点是,当要退出中断时,系统会查找就绪表是否有比处于中断服务状态任务的优先级更高的任务进入就绪状态。如果有将会一发一次调度,否则返回被中断的任务继续运行。关于中断的一些细节在后面的移植的部分还会讨论。
在所有的中断源中最重要的一个就是时钟中断,它为系统提供时间服务以此来实现任务的延时。

3.6.6 任务间的通信 对于一个完整的嵌入式操作系统来说,任务间的通信机制必不可少。μC/OS-II提供了相应的数据结构和机制来实现任务之间的同步和通信。


在ucos II 里任务间通信可以采用以下几种方式:
1. 共享全局变量,这是最快捷有效的方式,实现这种通信可以采用以下两种方式:一是利用宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()来关闭中断和打开中断,二是利用函数OSSchedLock()和OSSchedUnlock()对μC/OS-II中的任务调度函数上锁和开锁.
2. 使用信号量
3. 使用邮箱
4.  使用消息队列
创建一个任务需要给这个任务分配一个任务控制块,这个任务控制块存储着关于这个任务的重要信息。那么,事件控制块就好比任务里的任务控制块。它存储着这个事件的重要信息,我们说创建一个事件(信号,邮箱,消息队列),其本质的过程就是初始化这个事件控制块。多个任务可以同时等待同一个事件的发生。在这种情况下,当该事件发生后,所有等待该事件的任务中,优先级最高的任务得到了该事件并进入就绪状态,准备执行。
事件控制块是一个,其定义如下:
typedef struct {
    void   *OSEventPtr;                /* 指向消息或者消息队列的指针 */
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表      */
    INT16U  OSEventCnt;               /* 计数器(当事件是信号量时) */
    INT8U   OSEventType;                   /* 时间类型  */
    INT8U   OSEventGrp;               /* 等待任务所在的组  */
} OS_EVENT;
.OSEventPtr指针,只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构(因为队列要传递多个消息)。
.OSEventTbl[] 和 .OSEventGrp 很像前面讲到的OSRdyTbl[]和OSRdyGrp,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
.OSEventCnt 当事件是一个信号量时,.OSEventCnt是用于信号量的计数器。
(初始化时,如果信号量是用来表示一个或者多个事件的发生,那么该信号量的初始值应设为0, 如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1)
.OSEventType定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行的操作的正确性。
信号量是什么?信号量有什么用?
信号量一是可以用来表示一个或多个事件的发生,二是用来对共享资源的访问。
有时候邮箱可以当做信号量来使用,邮箱相对信号量而言,只是多传递了一个指针变量。其实和创建一个信号量的过程几乎是一样的,先申请一个空事件控制块,接着初始化这个事件控制块。最后返回一个指向这个事件控制块的指针。不同之处在于事件控制块的类型被设置成OS_EVENT_TYPE_MBOX,以及使用.OSEventPtr域来容纳消息指针。
如果把邮箱比作是信号量的升级版,那消息队列就是邮箱的升级版。邮箱可以实现从一个任务向另外一个任务发送一个指针变量,消息队列则可以实现从一个任务向另外一个任务发送很多个指针变量。而且每个指针指向的数据结构变量也可以有所不同。(消息队列最根本的部分是一个循环缓冲区,其中的每个单元包含一个指针。)和创建邮箱,创建信号量过程是很相似的,首先申请控制块,接着初始化这个控制块,和创建邮箱,信号量不同的,创建消息队列过程是多申请了一个队列控制块。
返回列表