FreeRTOS
内存管理
内核对象:task,queue,信号量,event group
Head_1
它只实现了pvPortMalloc,没有实现vPortFree。
如果不需要删除的内核对象,可以使用head_1:
- 实现最简单
- 没有碎片化
- 一些要求非常严格的系统里,不允许使用动态内存,就可以使用head_1
Head_2
heap_2 也是在数组上分配内存,跟Heap_1不一样地方在于:
1.Heap_2使用最佳匹配算法(best fit)来分配内存。
缺点碎片化 , 与Heap_4相比,Heap_2不会合并相邻的空闲内存,所以有严重的碎片化的问题
但是,如果申请,分配内存是大小总是相同的,这类情境下Heap_2没有碎片化的问题。
heap_3
heap_3 使用标准C库里的malloc ,free 函数,所以堆大小由链接器的配置决定。
与C库里的malloc,free函数并非线程安全的,Heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全。
heap_4
跟Heap_1、Heap_2一样,Heap_4 也是使用大数组分配内存。
Heap_4 使用首次适应算法(first fit)来分配内存,它还会把相邻的空闲内存合并一个更多的空闲内存,有助于较小内存的碎片问题。
首次适应(First-Fit)
定义:首次适应策略从内存的低地址开始,顺序扫描空闲块表,找到第一个大小足够满足请求的空闲块。
最佳适应(best-Fit)
定义:最佳适应策略会扫描整个空闲块表,找到最小的且能满足请求的空闲块,即最接近请求大小的空闲块。
Heap_5
Heap_5 分配内存、释放内存的算法跟 Heap_4 是一样的。
相比于 Heap_4,Heap_5 并不局限于管理一个大数组:它可以管理多块、分隔开的内存
任务管理
任务状态
running 和 not running 状态
ready就绪,随时可以运行
blocked阻塞,卡住了,母亲在等待同事回信息。
suspended:挂起,同事废话太多,不管他了。
优先级
我工作生活兼顾,喂饭、回消息优先级一样,轮流做。
我忙里偷闲:还有空闲任务,休息一下
厨房着火了,什么都别说了,先灭火,优先级更高。
栈(Stack)
喂小孩时,我要记得上一口喂了米饭,这口要喂青菜了
回信息时,我要记得刚才聊的是啥
做不同的任务,这些细节不一样
对于人来说,当然是记在脑子里
对于程序,是记在栈里
每个任务有自己的
事件驱动
孩子吃饭太慢了:先休息一会,等他咽下去了,等他提醒我了,再喂下一口
协调式调度(Co-operative Scheduling)
在给同事回消息
同事说:好了,你先去给小孩喂一口饭吧,你才能离开
同事不放你走,即使孩子哭了你也不能走
你好不容易可以给孩子喂饭了
孩子说:好了,妈妈你去处理一下工作吧
孩子不放你走,即使同事连发新消息你也不能走。
任务的删除
删除任务是使用的函数如下:
void vTaskDelete(TaskHandle_t xTaskToDelete )
pvTaskCode ———— 任务句柄,使用 xTaskCreate 创建任务时可以得到一个句柄,也可传入 NULL,这表示删除自己.。
怎么删除任务?
自杀:vTaskDelete(NULL)
被杀: 别的任务执行vTaskDelete(pvTaskCode),pvTaskCode 是自己的句柄。
杀人:执行 vTaskDelete(pvTaskCode),pvTaskCode 是别的任务的句柄
在学习调度方法之前,你只要初略地知道
FreeRTOS会确保最高优先级,可运行的任务,马上就能执行。
对于相同优先级的,可运行的任务,轮流执行。
3.4 任务状态
非运行状态
- 阻塞状态(Blocked)
- 暂停状态(Suspended)
- 就绪状态(Ready)
在日常生活例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复,那么母亲的工作被卡住了、被堵住了、处于阻塞状态(Blocked)。 重点在于:母亲在等待。
在实际产品中,我们不会让一个任务一直运行,而是使用"事件驱动"的方法让它运行:
- 任务要等待某个事件,事件发生后它才能运行。
- 在等待时间过程中,它消耗CPU资源
- 在等待事件的过程中,这个任务就处于阻塞状态。
在阻塞的状态的任务,它可以等待两种类型的事件:
时间相关的事件
1.可以等待一段时间,我等2分钟
2. 也可以一直等待,直到某个绝对时间: 我等到下午3点。
同步事件:这事件由别的任务,或者是中断程序产生。
例子:任务A 等待任务B 给它发送数据
例子: 任务A 等待用户按下按键。
同步事件的来源有很多
1.队列(queue)
2.二进制信号量
3.计数信号量
4.互斥量
5.递归互斥量,递归锁
6.事件组
7.任务通知
暂停状态(Suspended)
在日常生活的例子中,母亲正在电脑前跟同时沟通,母亲可以暂停。
- 好烦啊,我暂停一会。
- 领导说:你暂停一会。
FreeRTOS 中的任务也可以进入暂停状态,唯一的方法是通过vTaskSuspend 函数。函数原型如下:
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
参数xTaskToSuspend 表示要暂停的任务,如果为NULL,表示暂停自己。
要退出暂停,只能由别人来操作:
1.别的任务调用:vTaskResume
2.中断函数调用:xTaskResumeFromISR
实际开发中,暂停状态用的不多
就绪状态(Ready)
这个任务完全准备好了,随时可以运行,只是还轮不到它。这时,它就处于就绪状态。
Delay 函数
两个Delay函数
- vTaskDelay: 至少等待指定个数的Tick Interrupt才能变为就绪状态。
- vTaskDelayUntil:等待到指定的绝对时刻,才能变为就绪状态
空闲任务及其钩子函数
介绍
空闲任务(Idle 任务)的作用: 释放被删除的任务的内存。
为什么必须由空闲任务?
它的任务都是事件驱动的:平时大部分时间处于阻塞状态。有可能我们自己创建所有任务都无法执行,但是调度器必须能找到一个可以运行的任务:所以我们要提供空闲任务。 在使用vTaskStartScheduler()函数来创建、启动调度器时,这个函数内部会创建空闲任务:
1.空闲任务优先级为0:他不能阻碍用户任务运行。
2.空闲任务要么处于就绪态,要么处于运行态,永远不会阻塞。
空闲任务的优先级为0,这意味着一旦某个用户的任务变为就绪状态,那么空闲任务马上被切换出去,让这个用户任务运行。在这种情况下,我们说用户任务“抢占”(Pre-empt)了空闲任务,这是由调度器实现的。
要注意的是,如果使用vTaskDelete()来删除任务,那么你就要确保空闲任务有机会执行,否则就无法释放被删除任务的内存。
我们可以添加一个空闲任务的钩子函数(Idle Task Hook Functions),空闲任务的循环每执行一次,就会调用一次钩子函数,钩子函数的作用有这些。
- 执行一些低优先级的,后台的,需要连续执行的函数。
- 测量系统的空闲时间:空闲任务被执行就意味着所有的高优先级都停止了,所以测量空闲任务占据的时间,就可以算出处理器的占用率。
- 让系统进入省电模式:空闲任务能被执行就意味着没有重要的事情要做,当然可以进入省电模式了