嵌入式实时操作系统RTOS_概述与任务管理原则
嵌入式操作系统概述
RTOS简介
-
在STM32的软件开发一节中,我们介绍了嵌入式系统的两种开发方式,现在为了实现更加复杂的功能,我们开始学习实时操作系统RTOS
-
嵌入式系统门类广泛,像是手机上用的安卓或iOS都是嵌入式操作系统,此处仅介绍RTOS嵌入式实时操作系统
RTOS是一种具有较高实时性的嵌入式操作系统,所谓实时性顾名思义,就是能在较短的确定时间内完成特定的系统功能或中断响应
-
使用实时操作系统,可以通过将系统要执行的操作分为多个任务,各个任务之间通过操作系统提供的系统服务进行交互,并且可以为不同的任务分配不同的优先级
-
由此,RTOS规避了我们之前所用的前后台系统模式的下列问题:
- CPU利用率低(如轮询时CPU被占满无法进行其他操作)
- 实时性低(无法区分任务的优先级,只能按顺序执行)
- 可靠性低(前台与后台需要依靠标志位进行交互,相互耦合)
任务
-
任务概述
任务,又称线程,是嵌入式操作系统的核心概念:把一个嵌入式系统分解成各个拥有独立功能的程序模块,各个任务之间相互协作,共同实现整个系统的功能
-
任务特性
- 独立性:任务之间不能直接进行数据交换,需要通过操作系统提供的接口函数进行协作
- 并行性:操作系统为各个任务虚拟了一个CPU,每个CPU各自执行各自的任务(作为对比,在不使用操作系统时,CPU一次只能执行一个任务,例如在执行延时操作时整个系统就不能执行其他操作了)
-
任务的状态
-
任务一经创建,就会在这四种状态中切换
-
运行态:当前正在执行的任务
运行态的任务拥有CPU的使用权;在任何情况下只能有一个任务处于运行态
-
就绪态:准备好运行的任务,也是任务在创建后默认的状态
一旦当前的任务被终止或者阻塞,下一个优先级最高的就绪态任务就会进入运行态
-
阻塞态:运行态的任务在需要等待某一个事件的发生或延时一定时间时,该任务处于阻塞态
当任务要等的东西到了,那就切换回就绪态
-
挂起态:当任务暂时不需要运行时,可以把任务暂停(即挂起),在需要运行时再恢复该任务
-
内核
-
嵌入式操作系统的核心,负责任务的调度与任务之间的通信与同步
-
任务的调度
- 抢占式调度:始终让处于就绪态的最高优先级的任务先运行
- 时间片调度:优先级不同时,按抢占式调度;优先级相同的任务,根据时间片的大小进行轮转运行(每个任务执行完指定数量的时间片或者发生阻塞时,放弃CPU的使用权,切换执行任务)
-
任务的上下文
- 任务的上下文具体指CPU寄存器:类似于人要读一篇文章中的一句话时,需要综合上下文来理解,执行一个任务也需要相应的“上下文”
- 当内核决定切换运行任务时,需要保存当前任务的上下文到任务自身的任务栈中,然后把下一个任务的上下文从任务栈中重新装入CPU的寄存器,再进行这个任务,这一过程即任务切换/上下文切换
任务的组成
-
任务函数
每个任务都要完成对应的功能,实现这个功能的函数就称为任务函数
每个任务函数相当于一个独立的前后台系统:一般是一个无限循环,在循环中至少调用一次接口函数以实现任务的调度与任务之间的交互
切换任务的实质就是从一个任务的任务函数中退出,切换到另一个任务的任务函数中执行
-
任务栈
由于任务的独立性,因此必须给每个任务提供单独的内存空间,称之为任务栈
其中保存在任务运行时用于保存任务的上下文,任务函数中的局部变量和任务调用普通函数时保存的返回地址和传入普通函数的入口参数
-
任务控制块
内核定义的一个数据结构,用于实现对任务的管理与调度
在这个数据结构中:包括了任务的基本属性,如任务栈指针、任务状态、任务优先级和指向任务函数的指针
事件
-
在嵌入式操作系统中,大量采用事件驱动的方法来编写任务
事件指的是某种具体的操作:如按键被按下,串口接收完一帧数据,定时时间到等
在嵌入式系统中,为了表示一个事件的发生,使用信号量、事件标志组、消息队列来表示某一个事件的发生
任务管理原则
任务的划分
-
由上文的介绍,不难发现任务是整个RTOS的核心,而在程序设计之初,怎样把设计目标合理地划分为不同任务是软件设计的关键所在
-
一般来说,按照不同的功能作为系统的不同单元来划分任务是基本原则
此外,还应该遵守的原则包括:
- 关键功能:系统的关键功能应该用一个独立的最高优先级任务或者使用中断服务程序来实现
- 触发事件相同:由相同事件触发的若干功能应该组合成一个任务,以免事件分发
- 运行周期相同:运行周期相同的任务应该组合成一个任务,以免时间分发
- 功能聚合:关系密切的功能可以组成成一个任务,提高执行效率
- 耗时操作:耗时较多的功能应该单独设计为一个任务并设置为较低的优先级
- 固定顺序任务:如果一系列功能是按顺序执行的,那应该把这些功能组合成一个任务,以免任务间接力通信的麻烦
任务函数的结构
-
划分好任务后进行任务设计,主要的工作是设计任务函数与相关的数据类型,任务函数的具体结构可分为以下三类
-
单次执行任务
创建后只执行一次,执行结束后自动删除的任务
void task1(void *pdata) { 准备进行工作的代码//初始化变量与硬件设备 任务实体代码//完成该任务的具体功能 调用任务删除函数//把自己删除,操作系统不再管理 } -
周期执行任务
按固定的周期来执行,一般由一个无限循环构成,在循环中调用操作系统提供的延时函数以实现周期执行
void task2(void *pdata) { 准备进行工作的代码//初始化变量与硬件设备 while(1){ 任务实体代码//完成该任务的具体功能 调用系统延时函数//调用osDelay } } -
事件触发任务
需要等待某个事件发生,才能从阻塞态变为就绪态的任务
void task3(void *pdata) { 准备进行工作的代码//初始化变量与硬件设备 while(1){ 调用获取事件的函数//等待信号量或消息 任务实体代码//完成该任务的具体功能 } }
任务优先级
-
优先级设置原则
任务优先级的设置,一般遵循如下原则
- 中断关联性:与中断服务有关的任务安排尽可能高的优先级,以提高系统的实时性
- 紧迫性:对响应时间要求越严格,优先级就应该越高
- 关键性:任务越关键,优先级就应该越高
- 频繁性:周期性任务执行越频繁,周期越短,允许耽误的时间也就越少,应该安排较高的优先级
- 快捷性:在前面各项条件相近时,耗时越短的任务安排的优先级越高,以缩短其他任务的等待时间
- 传递性:信息的上游任务优先级应该高于下游的任务
-
优先级设置惯例
根据上文规则,可以总结如下经验:
-
中断关联任务(通过中断服务程序触发的任务)应该设置为最高优先级
因为中断的优先级是高于一切任务的,将中断关联任务设置为高优先级以防
理论上,这样的任务操作应该在中断中完成,但为了使中断快进快出,把处理工作单独剥离出来放在一个任务里执行
-
输入类的后台任务(按键检测、触摸检测、USB或串口消息处理)优先级设置较高,以保证信息的及时响应
-
输出类的周期性任务(GUI显示、LED数码管等)可以设置为低优先级
-
本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/artlessist/p/19411975
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外

浙公网安备 33010602011771号