uCOS操作系统与裸机程序最大的不同点是什么?嵌入式操作系统之UCOS-II总览

uCOS操作系统与裸机程序最⼤的不同点是什么?嵌⼊式操作系统之UCOS-II总
由于操作系统⼀般都是很⼤的,⽽且牵扯的问题⽐较多,所以今天只是⼀个⼤体上的对ucos的总结。
⼀、嵌⼊式操作系统概览
嵌⼊式操作系统的主要好处就是屏蔽了底层硬件的差别,给上层应⽤提供统⼀的接⼝,并管理进程调度和资源(如CPU 时间、内存)分配等。并且可以充分利⽤硬件资源,如在单任务时(⼤循环结构,如⼤部分51程序)遇到delay函数
时,CPU在空转。⽽在多任务系统,遇到delay或需等待资源时系统会⾃动运⾏下⼀个任务,等条件满⾜再回来运⾏先前的任务,这样就充分利⽤了CPU,提⾼了效率。
uC/OS操作系统与裸机程序的最⼤不同点就在于uC/OS有任务调度,可以根据任务的重要程度(优先级)优先执⾏重要的任务,从⽽确保能及时处理最重要的数据。(所以对于⼀个系统有必要使⽤OS的判断是能否划分⼀个个的任务,并且各任务间的耦合很⼩)
可以思考下裸机程序中断的时候发⽣的过程。利⽤堆栈可以很⾃由的在A、B中切换,如果切换⾜够快,
A、B看以来好像同时在执⾏,这就是并⾏,A、B就是任务。如果这个切换操作放到定时器函数中来做,就可以严格按照时间来切换。另外,各个任务之间有存在⼀定的关系,有逻辑上的先后等,必须引进全局的结构体、变量来标记⼀些信息,全局的这些数据是不会被释放的,所以所有的任务可以去通过读、写这些数据来实现各个程序块交流信息,实现所谓的同步、互斥。这就是操作系统的原理,⽽这些不同的通信⽅式按功能细分就成事件管理、内存管理等。
⼆、ucos的运⾏概览
⾸先是主函数,然后是OSInit(),这个函数就是对那些全局的数据结构初始化,建⽴希望的链表等数据结构,为后⾯全局变量通信做好准备,并且创建了1-2个系统任务(空闲任务必须,为了不让操作系统返回。统计任务可选),⽽所谓的创建任务OSTaskCreate就是把⼀个函数的函数地址、⾃⼰的栈建⽴联系、优先级、任务控制块等弄好,为任务切换做好准备。设置好定时切换的相关信息类似定时器,按照节拍在中断中进⾏任务切换判断(这个节拍是给延时函数提供计时基准,⼀个任务的延时时间到或等待的资源满⾜⽽进⼊就绪表就会检查优先级看是否可以执⾏),可以的话就发⽣切换,这个时候还没有开启开关,所以等任务创建完成后,启动多任务函数OSStart(),这个函数是让SP指到其中的⼀个栈,然后出栈就跳到⼀个任务函数⾥去了,接下来就是正常的任务运⾏了。
对于操作系统,主要是任务怎么释放CPU(延时、中断、等待资源),其他的任务怎么获得CPU(进⼊就绪表),如何到某个任务(优先级及任务控制列表)。
三、ucos各部分介绍
µC/OS-II的各种服务都以任务的形式来出现的。在µC/OS-II中,每个任务都有⼀个唯⼀的优先级。它是基于优先级可剥夺型内核,适合应⽤在对实时性要求较⾼的地⽅。
3.1 µC/OS-II的任务
µC/OS-II的核⼼部分就是它的任务,它也是通过任务来对不同事件进⾏响应和处理的。从代码上来看,µC/OS-II的任务⼀般为如下形式(C语⾔描述,后同):偏心轮机构
void uCOSTask(void *p)
行为监控{
while(1)
{
任务具体的功能;
}
}
}
创建任务的函数有个是OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U
prio),OSTaskCreate()需要四个参数:task是任务代码的指针,pdata是当任务开始执⾏时传递给任务的参数的指
针,ptos是分配给任务的堆栈的栈顶指针,prio是分配给任务的优先级。
µC/OS-II的任务是在内存中来看,任务由三个部分构成:任务的代码部分、任务堆栈和任务控制块。其中任务控制块保存任务的属性;任务堆栈在任务进⾏切换时保存任务运⾏的环境;任务代码部分就是宏观上看到的c语⾔代码。
嵌⼊式设备中⼀般只有⼀个处理器,所以在某⼀具体的时刻只能有⼀个任务占⽤处理器。µC/OS-II的任务⼀共有5种状态:睡眠、就绪、运⾏、等待和中断服务。
3.2 任务控制块
µC/OS-II中参与调度和管理的最⼩单位是任务。
⽽任务是通过任务控制块的形式管理的。任务控制块是⼀个结构体,它包含了任务的堆栈信息,任务控制块的指针,前⼀个任务控制块和后⼀个任务控制块的指针(利⽤优先级⼀个个查是否是要的任务,所以优先级是唯⼀的),任务的优先级(根据优先级查到任务控制块,从⽽就到该任务),任务需要等待的时间(任务延时的时候时钟节拍中断来的时候会对等待时间做减,为零的时候就放⼊就绪表,查看⼀下是否需要切换)等信息。
任务控制块包含了除了指向任务代码的所有信息。⽽任务的代码地址在任务运⾏时是怎么获得的呢?其实,任务代码的地址是通过任务的堆栈储存的。
3.3 任务堆栈
任务在创建时候,必须指明该任务的堆栈。任务的堆栈⼤⼩由⽤户根据实际情况⾃⾏定义。µC/OS-II的堆栈实际上是⼀个连续的内存块,任务在创建的时候,由函数OSTaskCreate()将任务的代码和⽤户为任务定义的堆栈联系起来。由于堆栈按照增长⽅向可以分为两种类型,故在创建任务的时候调⽤的堆栈初始化函数实际上也跟微处理器类型有关的。故这些代码也是移植时需要修改的。
钢架连栋大棚3.4系统任务
标本缸
µC/OS-II提供了两个系统任务:空闲任务和统计任务。其中空闲任务是必要的。因为在某⼀时刻可能所有的⽤户任务都不处于就绪状态,这样微处理器会因为没有任何任务运⾏造成系统崩溃。
3.5 临界区
µC/OS-II还有⼀个临界的概念,所谓临界区,就是⼀段特殊的代码。在这段代码内不允许中断的响应,以此来保证这段
µ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提供的系统函数。挂起任务使⽤函数OSTsakSuspe
nd(),恢复被挂起的任务使⽤函数OSTaskResume()。µC/OS-II还提供了任务的删除,优先级的修改,查询任务信息等其他功能的函数。
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域来容纳消息指针。
如果把邮箱⽐作是信号量的升级版,那消息队列就是邮箱的升级版。邮箱可以实现从⼀个任务向另外⼀个任务发送⼀个指针变量,消息队列则可以实现从⼀个任务向另外⼀个任务发送很多个指针变量。⽽且每个指针指向的数据结构变量也可以有所不同。(消息队列最根本的部分是⼀个循环缓冲区,其中的每个单元包含⼀个指针。)和创建邮箱,创建信号量过程是很相似的,⾸先申请控制块,接着初始化这个控制块,和创建邮箱,信号量不同的,创建消息队列过程是多申请了⼀个队列控制块。

本文发布于:2024-09-22 19:27:37,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/1/155677.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:任务   中断   就绪   事件   控制
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议