1、目录结构
2.FreeRTOS内存管理
heap(堆):被管理的内存
stack(栈):栈寄存器(sp)指向的内存
接口函数:(函数名:返回值类型 + 那个文件定义)
pvPortMalloc
vPortFree
heap_1
只实现了pvPortMalloc
程序不需要删除内核对象,那么可以使用heap_1:
实现最简单
没有碎片问题
一些要求非常严格的系统里,不允许使用动态内存,就可以使用heap_1
创建任务时:
task control block(TCB) 任务控制块
stack
A:创建任务之前整个数组都是空闲的
B:创建第1个任务之后,蓝色区域被分配出去了
C:创建3个任务之后的数组使用情况
heap_2
Heap_2 最佳匹配算法(best fit)来分配内存
支持vPortFree
最佳匹配算法:
a. 假设heap有3块空闲内存:5字节、25字节、100字节
b. pvPortMalloc想申请20字节
c. 找出最小的、能满足pvPortMalloc的内存:25字节
d. 把它划分为20字节、5字节
e. 返回这20字节的地址
f. 剩下的5字节仍然是空闲状态,留给后续的pvPortMalloc使用
不会合并相邻的空闲内存,所以Heap_2会导致严重的"碎片化"问题。
过程:
A:创建了3个任务
B:删除了一个任务,空闲内存有3部分:顶层的、被删除任务的TCB空间、被删除任务的Stack空
间
C:创建了一个新任务,因为TCB、栈大小跟前面被删除任务的TCB、栈大小一致,所以刚好分配
到原来的内存
#加粗样式# heap_3
Heap_3使用标准C库里的malloc、free函数,所以堆大小由链接器的配置决定,配置项
configTOTAL_HEAP_SIZE不再起作用
C库里的malloc、free函数并非线程安全的,Heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全。
heap_4
首次适应算法(first fit)来分配内存。它还会把相邻的空闲内存合并为一个更大的空闲内存,这有助于较少内存的碎片问题。
首次适应算法:
**a.**假设堆中有3块空闲内存:5字节、200字节、100字节
**b.**pvPortMalloc想申请20字节
**c.**找出第1个能满足pvPortMalloc的内存:200字节
**d.**把它划分为20字节、180字节
**e.**返回这20字节的地址
**f.**剩下的180字节仍然是空闲状态,留给后续的pvPortMalloc使用
过程:
A:创建了3个任务
B:删除了一个任务,空闲内存有2部分:顶层的被删除任务的TCB空间、被删除任务的Stack空间合并起来的
C:分配了一个Queue,从第1个空闲块中分配空间
D:分配了一个User数据,从Queue之后的空闲块中分配
E:释放的Queue,User前后都有一块空闲内存
F:释放了User数据,User前后的内存、User本身占据的内存,合并为一个大的空闲内存
heap_5
首次适应算法(first fit)
相比于Heap_4,Heap_5并不局限于管理一个大数组:它可以管理多块、分隔开的内存。
在嵌入式系统中,内存的地址可能并不连续,这种场景下可以使用Heap_5。既然内存时分隔开的,那么就需要进行初始化:确定这些内存块在哪、多大:
相关函数
void * pvPortMalloc( size_t xWantedSize ); //分配内存(返回值类型 + 那个文件定义)
void vPortFree( void * pv ); //释放内存
size_t xPortGetFreeHeapSize( void )
当前还有多少空闲内存,这函数可以用来优化内存的使用情况。比如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。
size_t xPortGetMinimumEverFreeHeapSize( void );
返回:程序运行过程中,空闲内存容量的最小值。
3、FreeRTOS任务管理
创建/删除
任务优先级和Tick
任务状态
Delay
空闲任务及钩子任务
调度算法
4、 队列(queue)
5、信号量
6、互斥量
7、事件组(event group)
8、任务通知
9、软件定时器(software timer)