大纲:
FreeRTOS是什么?
内核与操作系统的关系?
实时操作系统与非实时操作系统的区别?
FreeRTOS与Linux的一个重要区别
FreeRTOS中任务的概念?
FreeRTOS的任务是如何调度的?
FreeRTOS任务调度的三种策略和三种方式?
FreeRTOS任务之间如何通信?
空闲任务是什么?为什么需要空闲任务?空闲任务何时创建?
什么是优先级反转?什么是优先级继承?
守护任务是什么?
常见错误总结
项目经历的思考与改进
#FreeRTOS是什么?
FreeRTOS是一个开源免费的嵌入式实时内核(或实时调度程序),可以构建应用程序以满足其实时的苛刻要求。 它允许应用程序
组织为独立执行线程(在这里用任务代替线程的概念)的集合。 一个处理器只有一个核心,在任何一个时间点,只有一个任务能被执行。
程序员通过设计使得内核通过检查每个任务的优先级来调度每个任务。
#内核与操作系统的关系?
操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。
内核指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。内核是一个操作系统的核心,是操作系统最基本的部分。它负责管理系统的进程、内存、设备驱动程序、文件和网络系统等,决定着系统的性能和稳定性。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间。直接对硬件操作是非常复杂的,所以内核通常提供一种硬件抽象的方法来完成这些操作。硬件抽象隐藏了复杂性,为应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。
一个内核不是一套完整的操作系统。比如一套基于Linux内核的完整操作系统叫作Linux操作系统,或是GNU/Linux。
所以一套基于FreeRTOS内核的完整操作系统叫作FreeRTOS操作系统。
参考:
https://zhidao.baidu.com/question/505909415.html
#实时操作系统与非实时操作系统的区别?
实时操作系统任务执行的时间是可以确定的,但是非实时操作系统任务执行时间不确定。
形象的对比可以参考:
https://blog.csdn.net/u013752202/article/details/53649047
#FreeRTOS与Linux的一个重要区别
Linux有虚拟内存和进程的概念,FreeRTOS没有。
#FreeRTOS中任务的概念?
FreeRTOS中的任务是一个可执行的程序单元。每个任务拥有自己独立的一个栈,用来维护自身的运行状态。
任务有两大种状态:
1.运行态(Running)
定义:任务获得CPU使用权。
2.非运行态(No Running)
定义:任务没有获得CPU使用权。
通过细分还可以继续划分出3种子状态。
l 阻塞态(Blocked State)。
定义:任务在等待一个事件的到来而进入“阻塞”状态。
有两种事件能触发进入阻塞态:
1.时间事件。等待延时结束
2.同步时间。等待另一个任务,或者中断。比如:
消息队列、二值信号量、数值信号量、互斥锁、递归锁、事件组、任务通知这些用来
创建同步的事件。
l 挂起态(Suspended State)。
定义:任务不在任务管理器(scheduler)列表内。只有调用vTaskSuspend()才能使得任务进入挂起态,只有调用vTaskResume()或xTaskResumeFromISR()才能使任务离开挂起态。
l 就绪态(Ready State)
定义:任务不在运行态,既不在阻塞态,也不在挂起态则称作就绪态。此时他们不能运行,只是"准备好运行"。
#FreeRTOS的任务是如何调度的?
在程序开始时,先创建若干个任务,而此时任务调度器还没又开始运行,因此每一次任务创建后都会依据其优先级插入到就绪链表,同时保证全局变量 pxCurrentTCB 指向当前创建的所有任务中优先级最高的一个,但是任务还没开始运行。
在创建任务后,系统不会自动启动任务调度器,需要用户调用函数 vTaskStartScheduler 启动调度器。 该函数被调用后,会先创建系统自己需要用到的任务,比如空闲任务 prvIdleTask,定时器管理的任务等。
任务调度器是能切换一个任务进入与离开运行态的对象。当初始化完毕后,调用函数 vTaskStartScheduler启动任务调度器开始调度,此时,pxCurrentTCB所指的任务才开始运行。
当一个任务在运行态时,处理器执行该任务的代码。当一个任务不在运行态时,任务是休眠的,他的状态已经被保存,等待下一个时间段任务管理器决定它进入运行态从而恢复代码的执行。
#FreeRTOS任务调度的三种策略和三种方式
任务调度的三种策略:
1.抢占式(被抢占的任务只能等到高优先级的任务执行完后才能执行了)
2.时间片轮询(每一个时间片切换一次任务)
3.低功耗 configUSE_TICKLESS_IDLE,
组合起来有三种方式:
1.抢占式时间片
2.抢占式非时间片
3.合作模式
主要好处是可以通过enters the Blocked state 或者调用taskYIELD().控制任务调度,保护资源访问时避免因为任务调度而受到影响。
#FreeRTOS任务之间如何通信?
由于任务之间各自管理自己的栈,那任务之间通信就需要借助通信机制了。
FreeRTOS提供以下集中任务通信机制:消息队列、二值信号量、数值信号量、互斥锁、递归锁、事件组、任务通知。
#消息队列
消息队列是一个先进先出(FIFO)的缓冲区,数据从尾部写入,从头部取出。当然,也可以从头部写入,尾部取出。
消息队列可以被多个任务或者中断服务函数(ISR)读或者写。
使用消息队列的三步骤:
1.创建一个消息队列的对象。
2.向消息队列写入消息
3.从消息队列取出消息
具体的API用法参照手册。
#信号量
l 二值信号量
二值信号量就像一个长度为1的消息队列,里面要么存着0(空),要么存着1(满),故称二值信号量。
当一个任务调用获取信号量函数xSemaphoreTake(),任务马上中断程序的运行,尝试在一个阻塞的时间段内(可以是有限的,也可以是无限的)读取这个队列中的信号量。如果这个队列为空,导致任务进入阻塞态。
当事件发生时,中断或者另外一个任务调用给予信号量函数xSemaphoreGiveFromISR()或者xSemaphoreGive(),将信号量放入队列中,使得队列是满的。这马上导致等待信号量的任务离开阻塞态并从队列移除了信号量,当任务完成一次循环时,它再次尝试从队列中读取信号量,此时发现队列是空的,于是再次进入阻塞态等待下一个事件的发生。
使用二值信号量的三步骤:
1.创建信号量
2.给予信号量
3.获取信号量
l 数值信号量
正如二值信号量可以看成是一个长度为1的队列,数值信号量可以看成是长度大于1的队列。任务并不关心
该队列里的数据,只关心队列中对象(信号量)的数目。每当一个信号量被“给予”时,队列中的信号量就会加1;每当一个信号量被“获取”时,队列中的信号量就会减1。队列中的对象数,就是信号量的count值。
使用数值信号量步骤类似二值信号量。
l 互斥锁
互斥锁是一种特殊的二值信号量,用来控制在两个或者更多的任务访问同一个资源的权限。互斥锁可以
看成是一把钥匙,当一个任务合法地访问一个资源时,他必须成功拿到这把“钥匙”,在使用完资源后,
必须归还这把钥匙。这样其他的任务才能成功地对这个资源进行访问。
互斥锁和二值信号量的区别:
当信号量用作互斥锁时,通常有返回值。也就是使用完需要归还钥匙。
当信号量用作同步时,通常被丢弃且没有返回值。
l 递归锁
产生背景:
1.任务成功获取互斥锁。
2.持有互斥锁时,任务调用一个库函数。
3.库函数的实现尝试采用相同的互斥锁并进入
阻塞状态等待互斥体变为可用状态。
在这个场景结束时,任务处于阻塞状态,等待互斥量被返回,
但任务已经是互斥锁持有者。发生死锁是因为任务位于阻止状态等待自己。
递归锁为什么能解决死锁的问题?
递归互斥可以被同一个任务多次“取走”,并且只会在每次'give'之后再'take',才会被返回。
也就相当于允许'give'任务多把钥匙,所以任务能多次'take'钥匙。
#事件组
定义:事件组允许任务在阻塞状态下等待一个或多个组合
事件发生。
当多个任务正在等待相同事件或组合的所有任务或者当事件发生时,事件组能一下子解除所有阻塞的任务,
也就是说事件组能统一管理多个任务的信号量,并可以通过一定的逻辑运算做出相应的措施,比如事件组能一下子解除所有阻塞的任务。
#任务通知
前面介绍到,任务间通信可以通过队列、信号量、事件组等。这些都是需要借助“中间人”的角色间接实现。
FreeRTOS还提供任务间直接通信的方法,省去“中间人”的角色。
任务通知的特点:
一个任务中自带一个占用RAM固定大小为8字节的任务通知状态位,用于任务通知。
任务通知的优点:
明显比使用队列、信号量、事件组等需要创建“中间人”占用的RAM少。
任务通知的缺点:
1.任务通知的方向可以从中断到任务,但不能从任务到中断。而队列、信号量、事件组等则可以做到。
2.每次只能存一个数据。不像队列能存多个数据。
3.任务通知不能做到同时广播通知给多个任务。
#空闲任务是什么?为什么需要空闲任务?空闲任务何时创建?
假如:目前所有的任务都因为某种情况(如调用系统延时函数)进入阻塞态,此时必须有一个任务来运行,那此时就运行空闲任务。
另外,空闲任务能清除被删除的任务,回收内存
空闲任务会在调用vTaskStartScheduler()时自动创建。
#什么是优先级反转?什么是优先级继承?
优先级反转是指高优先级任务等待低优先级的任务释放互斥锁,导致中优先级任务能打断高优先级任务而执行。
优先级继承是指暂时提高拿着互斥锁的任务的优先级至最高,等它释放锁时,再将任务优先级恢复到原来。
#守护任务是什么?
守护任务又称定时器管理任务,在调用vTaskStartScheduler()时自动创建。负责接收借助“定时器命令队列”(Timer Command Queue)从普通任务发送到守护任务的命令。包括‘start a timer’, ‘stop a timer’ 和‘reset a timer’.
由于篇幅有限,常见错误总结 项目经历的思考与改进 放到下一讲。
参考:
[1]FreeRTOS官网手册.Mastering the FreeRTOS Real Time Kernel-- A Hands-On Tutorial Guide