FreeRTOS的堆内存管理源码分析

一、基础介绍

FreeRTOS是一个使用较为广泛的实时操作系统,主要组件包含:RTOS内核、任务管理、任务调度、中断管理、内存管理、定时器、队列、信号量、事件组等。参考的学习资料rtos.100ask.net,chat.deepseek.com,https://github.com/FreeRTOS/FreeRTOS-Kernel

二、堆内存管理

FreeRTOS实现了自己的堆内存管理,自定义了分配内存和释放内存的函数,共提供5个内存管理的方法,此处分析heap_1.cheap_4.c

三、heap_1.c的实现

  1. 特点:只分配不释放,时间确定
  2. malloc步骤:
    1. 分配一个大数组,表示heap
    2. 通过用户给定的分配空间的大小,保证内存对齐的前提下,计算实际分配空间的大小;
    3. 挂起所有任务,保证线程安全;
    4. 实际的分配操作,如果是第一次分配,对堆内存的首地址进行内存对齐;之后设置malloc函数的返回值位分配后内存块的首地址pvReturn,使用全局变量xNextFreeByte记录未分配堆内存的首地址;
    5. 恢复所有任务;
    6. 设置malloc失败的钩子函数(用户可配置)。
    7. 返回pvReturn
  3. 根据用户给定的分配空间的大小,计算得到内存对齐后的实际分配空间的操作
    1. portBYTE_ALIGNMENT = 8表示8字节对齐,和实际的芯片位数有关;
    2. portBYTE_ALIGNMENT_MASK = 0x07表示对齐掩码,address&portBYTE_ALIGNMENT_MASK!=0x00认为是内存不对齐的;
    3. xWantedSize += portBYTE_ALIGNMENT-(xWantedSize&portBYTE_ALIGNMENT_MASK)计算得到实际需要分配的堆内存大小(不考虑内存溢出),举个例子,用户传入分配空间大小为10,实际分配空间大小为10+8-(10&0x07)=16,形象理解为原地址先右偏8个字节再往下内存对齐。
  4. 对大数组heap进行内存对齐的操作(第一次分配的时候需要),heap_1.cheap_4.c有两种不同的写法,但实际是同样的操作
    1. pucAlignedHeap = (uint8_t*)( ((portPOINTER_SIZE_TYPE)&ucHeap[portBYTE_ALIGNMENT-1]) & (~(portPOINTER_SIZE_TYPE)portBYTE_ALIGNMENT_MASK) )
    2. uxStartAddress = ( portPOINTER_SIZE_TYPE ) ucHeap; uxStartAddress += ( portBYTE_ALIGNMENT - 1 ); uxStartAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
    3. 形象理解为原地址先右偏7个字节再向下内存对齐。
  5. heap_1.c的free函数,是个空函数。

四、heap_4.c的实现

  1. 特点:时间不定、相邻内存可合并
  2. malloc步骤:
    1. 分配一个大数组,表示heap
    2. 定义一个链表节点,用来组织未被分配的空闲块,包括xStart*pxEnd(在堆内存的尾部被设置);
    3. 通过用户给定的分配空间的大小,保证内存对齐的前提下,计算实际分配空间的大小(计算了链表节点的大小);
    4. 挂起所有任务,保证线程安全;
    5. 实际的分配操作,如果是第一次调用,使用prvHeapInit()初始化;遍历链表,寻找第一个有足够空间的空闲块,分配空间,同时如果剩余的空间大小大于heapMINIMUM_BLOCK_SIZE,则将剩余的空间重新组织,放入空闲块链表中;
    6. 设置剩余空闲空间的大小,分配最小空间的大小,用作其他两个函数使用;
    7. 恢复所有任务;
    8. 设置malloc失败的钩子函数(用户可配置)。
    9. 返回pvReturn
  3. free步骤:
    1. 挂起所有任务,保证线程安全;
    2. 设置全局变量,完成释放操作,并将链表节点使用函数prvInsertBlockIntoFreeList放到空闲链表中;
    3. 恢复所有任务。
  4. prvInsertBlockIntoFreeList(将相邻内存合并操作放到这里)
    1. 遍历找到左位置;
    2. 左合并,pxIterator + pxIterator->xBlockSize == pxBlockToInsert;
    3. 右合并,pxBlockToInsert + pxBlockToInsert->xBlockSize == pxIterator->pxNextFreeBlock;
    4. 不合并,pxIterator->pxNextFreeBlock = pxBlockToInsert

五、API总结

  1. void * pvPortMalloc( size_t xWantedSize );分配
  2. void vPortFree( void * pv );释放
  3. size_t xPortGetFreeHeapSize( void );获得当前的空闲内存
  4. size_t xPortGetMinimumEverFreeHeapSize( void );获得运行过程中空闲内存的最小值
  5. vApplicationMallocFailedHook();malloc失败的钩子函数
posted @ 2025-05-28 22:34  gramming  阅读(346)  评论(0)    收藏  举报