https://blog.csdn.net/qq_39382769/article/details/80863142(问题)
https://blog.csdn.net/nbawj/article/details/89083752(面试问题)
https://blog.csdn.net/zhzht19861011/category_9265276.html(FreeRTOS专题)
https://blog.csdn.net/awujiang/article/details/94053478(任务调度)
https://blog.csdn.net/qqliyunpeng/category_6527184.html(最详细的FreeRTOS专题,含代码)
https://blog.csdn.net/m0_37621078/article/details/90721900(RTOS操作系统解析)
https://blog.csdn.net/zhzht19861011/article/details/51190095(FreeRTOS高级篇)
通常使用实时操作系统(RTOS),如MQXLite、MQX、FreeRTOS、uCOS-III、uCLinux、VxWorks、eCos
使用非实时嵌入式操作系统,例分时操作系统(Time-sharing Operating System),如Android、Linxu、iOS、Windows CE
那么实时操作系统和分时操作系统的主要区别的第一性是什么呢?
本质就在于设计操作系统的时候,架构机制必须支持是否需要在 特定的时间内完成某项任务。if yes,real-time os,otherwise no.
英文称Real Time Operating System,简称RTOS。
1.实时操作系统定义
实时操作系统(RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的操作系统。因而,提供及时响应和高可靠性是其主要特点。实时操作系统有硬实时和软实时之分,硬实时要求在规定的时间内必须完成操作,这是在操作系统设计时保证的;软实时则只要按照任务的优先级,尽可能快地完成操作即可。我们通常使用的操作系统在经过一定改变之后就可以变成实时操作系统。
实时操作系统是保证在一定时间限制内完成特定功能的操作系统。例如,可以为确保生产线上的机器人能获取某个物体而设计一个操作系统。在“硬”实时操作系统中,如果不能在允许时间内完成使物体可达的计算,操作系统将因错误结束。在“软”实时操作系统中,生产线仍然能继续工作,但产品的输出会因产品不能在允许时间内到达而减慢,这使机器人有短暂的不生产现象。一些实时操作系统是为特定的应用设计的,另一些是通用的。一些通用目的的操作系统称自己为实时操作系统。但某种程度上,大部分通用目的的操作系统,如微软的Windows NT或IBM的OS/390有实时系统的特征。这就是说,即使一个操作系统不是严格的实时系统,它们也能解决一部分实时应用问题。
2.实时操作系统的特征
1)多任务;
2)有线程优先级
3)多种中断级别
小的嵌入式操作系统经常需要实时操作系统,内核要满足实时操作系统的要求。
3.实时操作系统的相关概念
(1)基本概念
代码临界段:指处理时不可分割的代码。一旦这部分代码开始执行则不允许中断打入;
资源:任何为任务所占用的实体;
共享资源:可以被一个以上任务使用的资源;
任务:也称作一个线程,是一个简单的程序。每个任务被赋予一定的优先级,有它自己的一套CPU寄存器和自己的栈空间。典型地,每个任务都是一个无限的循环,每个任务都处在以下五个状态下:休眠态,就绪态,运行态,挂起态,被中断态;
任务切换:将正在运行任务的当前状态(CPU寄存器中的全部内容)保存在任务自己的栈区,然后把下一个将要运行的任务的当前状态从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行;
内核:负责管理各个任务,为每个任务分配CPU时间,并负责任务之间通讯。分为不可剥夺型内核于可剥夺型内核;
调度:内核的主要职责之一,决定轮到哪个任务运行。一般基于优先级调度法;
(2)关于优先级的问题
任务优先级:分为优先级不可改变的静态优先级和优先级可改变的动态优先级;
优先级反转:优先级反转问题是实时系统中出现最多的问题。共享资源的分配可导致优先级低的任务先运行,优先级高的任务后运行。解决的办法是使用“优先级继承”算法来临时改变任务优先级,以遏制优先级反转。
(3)互斥
虽然共享数据区简化了任务之间的信息交换,但是必须保证每个任务在处理共享共享数据时的排他性。使之满足互斥条件的一般方法有:关中断,使用测试并置位指令(TAS),禁止做任务切换,利用信号量。
因为采用实时操作系统的意义就在于能够及时处理各种突发的事件,即处理各种中断,因而衡量嵌入式实时操作系统的最主要、最具有代表性的性能指标参数无疑应该是中断响应时间了。中断响应时间通常被定义为:
中断响应时间=中断延迟时间+保存CPU状态的时间+该内核的ISR进入函数的执行时间[2]。
中断延迟时间=MAX(关中断的最长时间,最长指令时间) + 开始执行ISR的第一条指令的时间[2]。
--------------------------------------------------------------------------------------------------------------------------
分时操作系统 【词语】:分时操作系统
【注音】:fēn shí cāo zuò xì tǒng
【英文】:Time-sharing Operating System
【释义】:使一台计算机同时为几个、几十个甚至几百个用户服务的一种操作系统。把计算机与许多终端用户连接起来,分时操作系统将系统处理机时间与内存空间按一定的时间间隔,轮流地切换给各终端用户的程序使用。由于时间间隔很短,每个用户的感觉就像他独占计算机一样。分时操作系统的特点是可有效增加资源的使用率。例如UNIX系统就采用剥夺式动态优先的CPU调度,有力地支持分时操作。
产生分时系统是为了满足用户需求所形成的一种新型 OS 。它与多道批处理系统之间,有着截然不同的性能差别。用户的需求具体表现在以下几个方面: 人—机交互 共享主机 便于用户上机
分时系统的基本思想
时间片 :是把计算机的系统资源(尤其是 CPU时间)进行时间上的分割,每个时间段称为一个时间片,每个用户依次轮流使用时间片。
分时技术:把处理机的运行时间分为很短的时间片,按时间片轮流把处理机分给各联机作业使用。
分时操作系统:是一种联机的多用户交互式的操作系统。一般采用时间片轮转的方式使一台计算机为多个终端服务。对每个用户能保证足够快的响应时间,并提供交互会话能力。
设计目标: 对用户的请求及时响应,并在可能条件下尽量提高系统资源的利用率。
适合办公自动化、教学及事务处理等要求人机会话的场合。
工作方式:
一台主机连接了若干个终端;每个终端有一个用户在使用;交互式地向系统提出命令请求;系统接受每个用户的命令;采用时间片轮转方式处理服务请求;并通过交互方式在终端上向用户显示结果;用户根据上步结果发出下道命令
分时系统实现中的关键问题:及时接收。及时处理。
特征:
交互性:用户与系统进行人机对话。
多路性:多用户同时在各自终端上使用同一CPU。
独立性:用户可彼此独立操作,互不干扰,互不混淆。
及时性:用户在短时间内可得到系统的及时回答。
影响响应时间的因素:终端数目多少、时间片的大小、信息交换量、信息交换速度。
------------------------------------------------------------------------------------------------------------
实时操作系统又分为硬实时和软实时。硬实时就是要求在规定的时间内必须完成操作,硬实时系统不允许超时,在软实时里面处理过程超时的后果就没有那么严格。
RTOS操作系统的核心内容在于:实时内核
用户给每个任务分配一个任务优先级,任务调度器可以根据此优先级来决定下一刻应该运行哪个任务
FreeRTOS是一个迷你的实时操作系统内核。FreeRTOS系统相当的小巧,最小化的FreeRTOS内核仅包括3个.c文件和少数头文件,总共不到9000行代码,编译后的映像小于10KB。
FreeRTOS的代码可以分解为三个主要区块:任务,通讯和硬件接口,可实现任务管理、时间管理、信号量、消息队列、内存管理、记录功能等功能。
(1) FreeRTOS免费
(2)WIFI蓝牙等这些带协议栈的芯片模块都是用FreeRTOS
(3)很多厂商用FreeRTOS做公司的操作系统
(4) 简单,文件数量少
(5)内核支持抢占式、合作式、时间片调度
(6)提供了一个用于低功耗的Tickless模式
(7)系统的组件在创建时可以选择动态或者静态的RAM
freeRTOS有configUSE_TIME_SLICING,该config设置为0时,和您说的一致,该位为1时,如果任务一已经被调度执行了,那么会将时间片运行完,再执行高优先级的任务二
FreeRTOS 操作系统支持的任务调度方式:抢占式,时间片和合作式,这部分算是 FreeRTOS 操作系统的核心了
https://www.cnblogs.com/yangguang-it/p/7157072.html(重点!也解释了linux的时间片轮转)
1.任务控制模块(TCB)
当xTaskCreate()函数被调用时,一个任务被创建,并放入就绪列表。就绪列表是一个双向链表,在链表中每个任务按优先级排列,并且设置有记录相关信息的变量和进行各种操作的指针。
2.任务状态
运行:如果一个任务正在执行,那么说这个任务处于运行状态。此时它占用处理器。
就绪:就绪的任务已经具备执行的能力(不同于阻塞和挂起),但是因为有一个同优先级或者更高优先级的任务处于运行状态而还没有真正执行。
阻塞:如果任务当前正在等待某个时序或外部中断,我们就说这个任务处于阻塞状态。比如一个任务调用vTaskDelay()后会阻塞到延时周期到为止。任务也可能阻塞在队列或信号量事件上。进入阻塞状态的任务通常有一个“超时”周期,当事件超时后解除阻塞。
挂起:处于挂起状态的任务同样对调度器无效。仅当明确的分别调用vTaskSuspend() 和xTaskResume() API函数后,任务才会进入或退出挂起状态。不可以指定超时周期事件(不可以通过设定超时事件而退出挂起状态)
阻塞api函数:
任务延时:void vTaskDelay( const TickType_t xTicksToDelay )
xTicksToDelay,延时多少个心跳周期,延迟的任务进入阻塞态,经过指定的心跳周期后,转移到就绪态,将毫秒单位转换成心跳周期单位使用常量 portTICK_RATE_MS ,比如要延时 250ms,则 xTicksToDelay = 250 / portTICK_RATE_MS
周期性延时:void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime,
const TickType_t xTimeIncrement )
pxPreviousWakeTime,用于保存任务上一次离开阻塞态的时刻,这个时刻用于作为参考点计算任务下一次离开阻塞态的时刻,这个值自动更新,只需要付初始值即可,一般是 pxPreviousWakeTime = xTaskGetTickCount()
xTimeIncrement,循环周期时间,要延时 250ms,则 xTimeIncrement = 250 / portTICK_RATE_MS
【1】需要配置 INCLUDE_vTaskDelayUntil = 1,才能使用这个函数
【2】使用的时候,需要将这个函数放在循环中例如for(;;){vPrintString(pcTaskName);vTaskDelayUntil(...);}
【3】执行完任务后,任务本身进入阻塞态,等待时间的到达
【4】既然要作为周期任务,优先级可能要设置成最高才能实现
3.任务调度器
影响RTOS响应时间的一个重要方面就是任务调度算法。
简单的说,调度器就是使用相关的调度算法来决定当前需要执行的任务。所有的调度器有一个共同的
特性:
调度器可以区分就绪态任务和挂起任务(由于延迟,信号量等待,邮箱等待,事件组等待等原因而使
得任务被挂起)。
调度器可以选择就绪态中的一个任务,然后激活它(通过执行这个任务)。 当前正在执行的任务是运
行态的任务。
不同调度器之间最大的区别就是如何分配就绪态任务间的完成时间。
嵌入式实时操作系统的核心就是调度器和任务切换,调度器的核心就是调度算法。任务切换的实现在
不同的嵌入式实时操作系统中区别不大,基本相同的硬件内核架构,任务切换也是相似的。调度算法就有
些区别了。下面我们主要了解一下抢占式调度器和时间片调度器。
抢占式调度器基本概念
如果使用了抢占式调度,最高优先级的任务一旦就绪,总能得到 CPU 的控制权。 比如,当一个运行
着的任务被其它高优先级的任务抢占,当前任务的 CPU 使用权就被剥夺了,或者说被挂起了,那个高优
先级的任务立刻得到了 CPU 的控制权并运行。 又比如,如果中断服务程序使一个高优先级的任务进入就
绪态,中断完成时,被中断的低优先级任务被挂起,优先级高的那个任务开始运行。
使用抢占式调度器,使得最高优先级的任务什么时候可以得到 CPU 的控制权并运行是可知的,同时
使得任务级响应时间得以最优化。
总的来说,学习抢占式调度要掌握的最关键一点是:每个任务都被分配了不同的优先级,抢占式调度
器会获得就绪列表中优先级最高的任务,并运行这个任务。
FreeRTOS 抢占式调度器的实现
如果用户在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用时间片调度, 那么每个任务必须配
置不同的优先级。当 FreeRTOS 多任务启动执行后,基本会按照如下的方式去执行:
首先执行的最高优先级的任务 Task1, Task1 会一直运行直到遇到系统阻塞式的 API 函数,比如延迟,
事件标志等待,信号量等待,Task1 任务会被挂起,也就是释放 CPU 的执行权,让低优先级的任务
得到执行。
运行过程描述如下:
此时任务 Task1 在运行中,运行过程中由于 Task2 就绪,在抢占式调度器的作用下任务 Task2 抢占
Task1 的执行。 Task2 进入到运行态,Task1 由运行态进入到就绪态。
任务 Task2 在运行中,运行过程中由于 Task3 就绪,在抢占式调度器的作用下任务 Task3 抢占 Task2
的执行。 Task3 进入到运行态,Task2 由运行态进入到就绪态。
任务 Task3 运行过程中调用了阻塞式 API 函数,比如 vTaskDelay,任务 Task3 被挂起,在抢占式调
度器的作用下查找到下一个要执行的最高优先级任务是 Task2,任务 Task2 由就绪态进入到运行态。
任务 Task2 在运行中,运行过程中由于 Task3 再次就绪,在抢占式调度器的作用下任务 Task3 抢占
Task2 的执行。 Task3 进入到运行态,Task2 由运行态进入到就绪态。
时间片调度器基本概念
在小型的嵌入式 RTOS 中,最常用的的时间片调度算法就是 Round-robin 调度算法。这种调度算法
可以用于抢占式或者合作式的多任务中。另外,时间片调度适合用于不要求任务实时响应的情况。
实现 Round-robin 调度算法需要给同优先级的任务分配一个专门的列表,用于记录当前就绪的任务,
并为每个任务分配一个时间片(也就是需要运行的时间长度,时间片用完了就进行任务切换)。
ISR(滴答定时器中断、时基中断)可知,时间片的长度为四个时基,滴答定时器中断在每个时基结束都会发生,时间片轮转调度可以设置时基的整数倍作为时间片的长度。
任务1、2、3为相同优先级的三个任务,在在时间片轮转的调度下有序执行,至于他们的执行顺序,这是由调度队列来调控的,一个任务出队(执行)后会被插入到队尾,这样就实现了多个任务的循环执行。
(1)任务3正在执行,时基中断发生,但任务3还没有到期
(2)任务3主动放弃剩下的时间片
(3)UCOSIII恢复任务1,因为它是调度队列中任务3的下一个任务
(4)任务1执行直到它的时间片到期
(5) (6) (7) (8)同上;
FreeRTOS 时间片调度器的实现
在 FreeRTOS 操作系统中只有同优先级任务才会使用时间片调度,另外还需要用户在
FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_TIME_SLICING 1
默认情况下,此宏定义已经在 FreeRTOS.h 文件里面使能了,用户可以不用在 FreeRTOSConfig.h 文
件中再单独使能。
运行过程描述如下:
先运行任务 Task1,运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task2。
任务 Task2 运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task3。
任务 Task3 在运行期间调用了阻塞式 API 函数,调用函数时,虽然 5 个系统时钟节拍的时间片大小
还没有用完,此时依然会通过时间片调度切换到下一个任务 Task4。 (注意,没有用完的时间片不会
再使用,下次任务 Task3 得到执行还是按照 5 个系统时钟节拍运行)
任务 Task4 运行够 5 个系统时钟节拍后,通过时间片调度切换到任务 Task1。
上面就是一个简单的同优先级任务通过时间片调度进行任务调度和任务切换的过程。
Summary:
时间片调度和抢占式调度我们一般都是开启了的,这样优先级相同时,使用时间片调度,优先级不同时,使用抢占式调度。默认情况下,在freertos.h中使能了时间片调度:
而抢占式调度需要我们用户自己开启,一般在freertosconfig.h中使能:
4.任务优先级
RTOS的基本工作单元是任务,所有任务都有一个用户优先级,如:从0到最高,如:0(最低),1,2,3 (最高) 在FreeRTOSConfig.h中设置优先级个数。
每一个优先级都是一个列表,其中可以包含多个任务。
5.任务调度
https://blog.csdn.net/qqliyunpeng/article/details/53454188(更详细的代码分析)
要能够选择下一个运行的任务,调度器需要在每个时间片的结束时刻运行自己本身。一个称为心跳(tick,有些地方被称为时钟滴答,本文中一律称为时钟心跳)中断的周期性中断用于此目的。时间片的长度通过心跳中断的频率进行设定,心跳中断频率由FreeRTOSConfig.h 中的编译时配置常量 configTICK_RATE_HZ 进行配置。比如说,如果 configTICK_RATE_HZ 设为 1000(HZ),则时间片长度为 1ms。
心跳计数(tick count)值表示的是从调度器启动开始,心跳中断的总数,并假定心跳计数器不会溢出。用户程序在指定延迟周期时不必考虑心跳计数溢出问题,因为时间连
贯性在内核中进行管理。
系统节拍:FreeRTOS有一个定期的时钟节拍(通常是MS级) (在FreeRTOSConfig.h中设置),每个节拍中断释放时vTaskSwitchContext()函数就会被调用。它就会选择优先级最高的就绪任务并抢占优先级低的任务的CPU让它执行。这就是可剥夺式多任务内核。
保证高优先级任务顺畅执行是FreeRTOS最核心的部分
//找出就绪列表中优先级最高的任务
while( list LIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) )
{
configASSERT( uxTopReadyPriority );// uxTopReadyPriority设定好的高于或等于用户设置的最高优先级
--uxTopReadyPriority; //得到最高优先级数
}
listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB,&( pxReadyTasksLists[ uxTopReadyPriority ] ) ); // pxCurrentTCB指向最高优先级任务
//抢占同一优先级的下一个任务。
同时创建了两个相同的任务,然后任务中都有一段250ms的vTaskDelay延时,任务2优先级2,任务1优先级1,同时运行,则执行的图表表示如下:
行为说明:
任务1 创建在最高优先级,以保证其可以最先运行。任务1 首先打印输出两个字符串,然后将任务2 的优先级提升到自己之上。
任务2 一旦拥有最高优先级便启动执行(进入运行态)。由于任何时候只可能有一个任务处于运行态,所以当任务2 运行时,任务1 处于就绪态。
任务2 打印输出一个信息,然后把自己的优先级设回低于任务 1的初始值。
任务2 降低自己的优先级意味着任务1 又成为具有最高优先级的任务,所以任务1 重新进入运行态,任务2 被强制切入就绪态。
【1】从图表中可以看出任务高的优先级在高的位置,这种方式,我们理解的更容易
【2】对任务的优先级的再次更改需要任务创建时的任务句柄(最后一个参数)
【3】到这里我们可以看到,图表好的形式如下:
行为说明:
【1】两个相同优先级的任务在图中也分了高低,是为了好区分
【2】此图表表示的不是简单的创建两个相同的打印的函数,至少,保证了打印能完成才去调度,在图中红色段也至少含有几个tick
FreeRTOS中消息队列是任务间数据交换的常用手段(中断服务程序同样使用队列来通信与同步),消息队列是生产者消费者模型的重要组成部分
消息队列的数据结构如下:
typedef struct QueueDefinition
{
signed char *pcHead; //队头
signed char *pcTail; //队尾
signed char *pcWriteTo;
signed char *pcReadFrom;
xList xTasksWaitingToSend; //等待发送的任务列表
xList xTasksWaitingToReceive; //等待接收的任务列表
} xQUEUE;
如何使用消息队列:
xQueueHandle MsgQueue; 声明一个队列句柄,队列句柄可以理解成一个队列的标记,不同的队列具有不同的标记。
MsgQueue = xQueueCreate( 5 , sizeof( int16_t) );创建队列,即在内容中开辟固定大小的区域。
xQueueSend( MsgQueue, ( void* )&SendNum,0 );向队列中填充内容,第二参数需要取出地址并进行类型转换,第三个参数设置等待时间,在队列满的情况下再往队列中填充内容的话便会阻塞任务,直到等待时间溢出;若此处填充的内容为0的话,则立即返回插入队列结果(成功或失败)。
xQueueReceive( MsgQueue, &ReceiveNum,100/portTICK_RATE_MS )从队列中取出内容,第二个参数需要取出地址,第三个参数为等待最大时间,若在等待的时间内队列中没有数据则返回阻塞任务。
https://blog.csdn.net/awujiang/article/details/94053478