网络知识 娱乐 RTOS面试常问题目

RTOS面试常问题目

RTOS常见面试问题

  • RTOS的实时性是如何实现的
  • 任务之间是如何通信的
  • 二值信号量和互斥量的区别
  • 任务通知是怎么是实现的
  • RTOS内核是怎么调度的
  • FreeRTOS四种任务状态

RTOS的实时性是如何实现的

一个处理器核心在某一时刻只能运行一个任务,操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务。
实时操作系统中都要包含一个实时任务调度器,这个任务调度器与其它操作系统的最大不同是强调:严格按照优先级来分配CPU时间,并且时间片轮转不是实时调度器的一个必选项

FreeRTOS就是一款支持多任务运行的实时操作系统,具有时间片抢占式合作式三种调度方式。

  • 合作式调度,主要用在资源有限的设备上面,现在已经很少使用了。出于这个原因,后面的FreeRTOS版本中不会将合作式调度删除掉,但也不会再进行升级了。
  • 抢占式调度,每个任务都有不同的优先级,任务会一直运行直到被高优先级任务抢占或者遇到阻塞式的 API 函数,比如 vTaskDelay。
  • 时间片调度,每个任务都有相同的优先级,任务会运行固定的时间片个数或者遇到阻塞式的 API函数,比如vTaskDelay,才会执行同优先级任务之间的任务切换。

抢占式调度器
使用了抢占式调度,最高优先级的任务一旦就绪,总能得到 CPU 的控制权。 比如,当一个运行着的任务被其它高优先级的任务抢占,当前任务的 CPU 使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了 CPU 的控制权并运行。 又比如,如果中断服务程序使一个高优先级的任务进入就绪态,中断完成时,被中断的低优先级任务被挂起,优先级高的那个任务开始运行。使用抢占式调度器,使得最高优先级的任务什么时候可以得到 CPU 的控制权并运行是可知的,同时使得任务级响应时间得以最优化。
在这里插入图片描述

总的来说,学习抢占式调度要掌握的最关键一点是:每个任务都被分配了不同的优先级,抢占式调度器会获得就绪列表中优先级最高的任务,并运行这个任务。

在 FreeRTOS 的配置文件 FreeRTOSConfig.h 中禁止使用时间片调度,那么每个任务必须配置不同的优先级。当 FreeRTOS 多任务启动执行后,基本会按照如下的方式去执行:

1.首先执行的最高优先级的任务 Task1,Task1 会一直运行直到遇到系统阻塞式的 API 函数,比如延迟,事件标志等待,信号量等待,Task1 任务会被挂起,也就是释放 CPU 的执行权,让低优先级的任务得到执行。

2.FreeRTOS 操作系统继续执行任务就绪列表中下一个最高优先级的任务 Task2,Task2 执行过程中有两种情况:

  • Task1由于延迟时间到,接收到信号量消息等方面的原因,使得 Task1从挂起状态恢复到就绪态,在抢占式调度器的作用下,Task2的执行会被 Task1 抢占。
  • Task2 会一直运行直到遇到系统阻塞式的 API函数,比如延迟,事件标志等待,信号量等待,Task2任务会被挂起,继而执行就绪列表中下 一个最高优先级的任务。

3.如果用户创建了多个任务并且采用抢占式调度器的话,基本都是按照上面两条来执行。 根据抢占式调度器,当前的任务要么被高优先级任务抢占,要么通过调用阻塞式 API 来释放 CPU 使用权让低优先级任务执行,没有用户任务执行时就执行空闲任务。

任务之间是如何通信的

在FreeRTOS中所有的通信和同步机制都是基于队列(FIFO)实现的。但是也可以使用 LIFO 的存储缓冲,也就是后进先出,FreeRTOS 中的队列也提供了 LIFO 的存储缓冲机制
1.什么是队列
队列是数据结构中的一个概念,其最大的特性为
先进先出
(FIFO:First In First Out)

2. 队列特点:
1、多任务访问
队列不是属于某个特别指定的任务的,任何任务都可以向队列中发送消息,或者从队列中提取消息。
2、出队阻塞
当任务尝试从一个队列中读取消息的时候可以指定一个阻塞时间,这个阻塞时间就是当任务从队列中读取消息无效的时候任务阻塞的时间。
3、入队阻塞
入队说的是向队列中发送消息,将消息加入到队列中。

3. 队列创建
在使用队列之前必须先创建队列,有两种创建队列的方法,一种是静态的,使用函数xQueueCreateStatic();另一个是动态的,使用函数 xQueueCreate()
1、函数 xQueueCreate() 此函数本质上是一个宏,用来动态创建队列
2、函数 xQueueCreateStatic() 此函数也是用于创建队列的,但是使用的静态方法创建队列,队列所需要的内存由用户自行分配
3、函数 xQueueGenericCreate() 函数 ,用于动态创建队列,创建队列过程中需要的内存均通过FreeRTOS 中的动态内存管理函数 pvPortMalloc()分配

4. 队列初始化函数
队列初始化函数 prvInitialiseNewQueue()用于队列的初始化

在这里插入图片描述
5. 向队列发送消息
在这里插入图片描述
6. 从队列读取消息
在这里插入图片描述

二值信号量和互斥量的区别

信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量计数型信号量互斥信号量递归互斥信号量,不同的信号量其应用场景不同。

1. 信号量简介
信号量常常用于控制对共享资源的访问和任务同步

2. 二值信号量
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些细微的差别,互斥信号量拥有优先级继承机制二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步),而互斥信号量适合用于简单的互斥访问,

和队列一样,信号量 API 函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数

二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值的吗? 任务和中断使用这个特殊队列不用在乎队列中存的是什么消息,只需要知道这个队列是满的还是空的。可以利用这个机制来完成任务与中断之间的同步。

在这里插入图片描述在这里插入图片描述
3. 互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量,互斥信号量适合用于那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。

当一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先级翻转”的影响降到最低

● 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
● 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

4.计数型信号量
同二值信号量一样,用户不需要关心队列中存储了什么数据,只需要关心队列是否为空即可。计数型信号量通常用于如下两个场合:
1、事件计数
在这个场合中,每次事件发生的时候就在事件处理函数中释放信号量(增加信号量的计数值),其他任务会获取信号量
2、资源管理
在这个场合中,信号量值代表当前资源的可用数量,比如停车场当前剩余的停车位数量。一个任务要想获得资源的使用权,首先必须获取信号量,信号量获取成功以后信号量值就会减一。当信号量值为 0 的时候说明没有资源了。当一个任务使用完资源以后一定要释放信号量,释放信号量以后信号量值会加一。在这个场合中创建的计数型信号量初始值应该是资源的数量,比如停车场一共有 100 个停车位,那么创建信号量的时候信号量值就应该初始化为 100。

任务通知是怎么是实现的

1. 任务通知简介
任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏 configUSE_TASK_NOTIFICATIONS 定义为 1。 FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任 务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
● 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
● 覆盖接收任务的通知值。
● 更新接收任务通知值的一个或多个 bit。
● 增加接收任务的通知值。

RTOS内核是怎么调度的

FreeRTOS四种任务状态

FreeRTOS的任务状态(4种)
1.运行态(Running) 2.就绪态(Ready) 3.阻塞态(Blocked) 4.挂起态(Suspended)
Running—运行态
当任务处于实际运行状态被称之为运行态,即 CPU 的使用权被这个任务占用。
Ready—就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。
Blocked—阻塞态
由于等待信号量,消息队列,事件标志组等而处于的状态被称之为阻塞态,另外任务调用延迟函数也会处于阻塞态。
Suspended—挂起态
类似阻塞态,通过调用函数 vTaskSuspend()对指定任务进行挂起,挂起后这个任务将不被执行,只有调用函数 xTaskResume()才可以将这个任务从挂起态恢复。
在这里插入图片描述