FreeRTOS的堆内存管理源码分析
一、基础介绍
FreeRTOS是一个使用较为广泛的实时操作系统,主要组件包含:RTOS内核、任务管理、任务调度、中断管理、内存管理、定时器、队列、信号量、事件组等。参考的学习资料rtos.100ask.net,chat.deepseek.com,https://github.com/FreeRTOS/FreeRTOS-Kernel。
二、堆内存管理
FreeRTOS实现了自己的堆内存管理,自定义了分配内存和释放内存的函数,共提供5个内存管理的方法,此处分析heap_1.c和heap_4.c
三、heap_1.c的实现
- 特点:只分配不释放,时间确定
- malloc步骤:
- 分配一个大数组,表示
heap; - 通过用户给定的分配空间的大小,保证内存对齐的前提下,计算实际分配空间的大小;
- 挂起所有任务,保证线程安全;
- 实际的分配操作,如果是第一次分配,对堆内存的首地址进行内存对齐;之后设置malloc函数的返回值位分配后内存块的首地址
pvReturn,使用全局变量xNextFreeByte记录未分配堆内存的首地址; - 恢复所有任务;
- 设置malloc失败的钩子函数(用户可配置)。
- 返回
pvReturn。
- 分配一个大数组,表示
- 根据用户给定的分配空间的大小,计算得到内存对齐后的实际分配空间的操作
portBYTE_ALIGNMENT = 8表示8字节对齐,和实际的芯片位数有关;portBYTE_ALIGNMENT_MASK = 0x07表示对齐掩码,address&portBYTE_ALIGNMENT_MASK!=0x00认为是内存不对齐的;xWantedSize += portBYTE_ALIGNMENT-(xWantedSize&portBYTE_ALIGNMENT_MASK)计算得到实际需要分配的堆内存大小(不考虑内存溢出),举个例子,用户传入分配空间大小为10,实际分配空间大小为10+8-(10&0x07)=16,形象理解为原地址先右偏8个字节再往下内存对齐。
- 对大数组heap进行内存对齐的操作(第一次分配的时候需要),
heap_1.c和heap_4.c有两种不同的写法,但实际是同样的操作pucAlignedHeap = (uint8_t*)( ((portPOINTER_SIZE_TYPE)&ucHeap[portBYTE_ALIGNMENT-1]) & (~(portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK) )uxStartAddress = ( portPOINTER_SIZE_TYPE ) ucHeap; uxStartAddress += ( portBYTE_ALIGNMENT - 1 ); uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );- 形象理解为原地址先右偏7个字节再向下内存对齐。
heap_1.c的free函数,是个空函数。
四、heap_4.c的实现
- 特点:时间不定、相邻内存可合并
- malloc步骤:
- 分配一个大数组,表示
heap; - 定义一个链表节点,用来组织未被分配的空闲块,包括
xStart和*pxEnd(在堆内存的尾部被设置); - 通过用户给定的分配空间的大小,保证内存对齐的前提下,计算实际分配空间的大小(计算了链表节点的大小);
- 挂起所有任务,保证线程安全;
- 实际的分配操作,如果是第一次调用,使用
prvHeapInit()初始化;遍历链表,寻找第一个有足够空间的空闲块,分配空间,同时如果剩余的空间大小大于heapMINIMUM_BLOCK_SIZE,则将剩余的空间重新组织,放入空闲块链表中; - 设置剩余空闲空间的大小,分配最小空间的大小,用作其他两个函数使用;
- 恢复所有任务;
- 设置malloc失败的钩子函数(用户可配置)。
- 返回
pvReturn。
- 分配一个大数组,表示
- free步骤:
- 挂起所有任务,保证线程安全;
- 设置全局变量,完成释放操作,并将链表节点使用函数
prvInsertBlockIntoFreeList放到空闲链表中; - 恢复所有任务。
- prvInsertBlockIntoFreeList(将相邻内存合并操作放到这里)
- 遍历找到左位置;
- 左合并,
pxIterator + pxIterator->xBlockSize == pxBlockToInsert; - 右合并,
pxBlockToInsert + pxBlockToInsert->xBlockSize == pxIterator->pxNextFreeBlock; - 不合并,
pxIterator->pxNextFreeBlock = pxBlockToInsert。
五、API总结
void * pvPortMalloc( size_t xWantedSize );分配void vPortFree( void * pv );释放size_t xPortGetFreeHeapSize( void );获得当前的空闲内存size_t xPortGetMinimumEverFreeHeapSize( void );获得运行过程中空闲内存的最小值vApplicationMallocFailedHook();malloc失败的钩子函数

浙公网安备 33010602011771号