内存动态分配

限于喜欢 提交于 2020-01-06 18:42:50

在单片机中由于内存资源紧张,不可能给每个任务分配专有的内存区,尤其是涉及到通讯模块的程序,对内存的使用更是敏感。为此开发一个简单的内存管理库,对以后的开发还是有着不小的帮助的。

功能实现:内存动态分配、内存动态释放、内存碎片回收

 

heap.c

// 内存划分:申请的一块内存分为两部分,一部分用于存储链表结构体,另一部分给用户使用
// 内存管理:链表不存在头节点和尾节点,理论上在内存在可以存在无数个节点,通过空闲标志位来识别该内存块是否可用
#include "heap.h"
#define HEAP_TRUE   1
#define HEAP_FALSE   0
#pragma pack(push, 1)
typedef struct _sHeapList_t
{
  unsigned int size; // 内存块大小
  unsigned char isfree; // 空闲标志
  unsigned char *penter; // 内存入口地址
  struct _sHeapList_t *plast; // 指向上一个节点,头节点为NULL
  struct _sHeapList_t *pnext; // 指向下一个节点,尾节点尾NULL
}sHeapList_t;
#pragma pack(pop)

// 申请一个内存块,作为基础内存
static unsigned char Heap[HEAP_TOTAL_SIZE];

// 内存申请(注:函数不可重入)
void *pmalloc(unsigned int size)
{
  sHeapList_t *pnode = (sHeapList_t *)Heap;
  // 内存管理模块未初始化时需要执行初始化(以第一个节点是否存在作为判断依据)
  if(pnode->penter != (Heap + sizeof(sHeapList_t)))
  {
     pnode->plast = NULL;
     pnode->pnext = NULL;
     pnode->isfree = HEAP_TRUE;
     pnode->penter = Heap + sizeof(sHeapList_t); // 入口地址向后偏移一个结构体的位置
     pnode->size = HEAP_TOTAL_SIZE - sizeof(sHeapList_t); // 剩余内存需要减去一个结构体大小的空间
  }
  if(size > 0)
  {
    // 搜索一个不小于指定大小的内存块
    while(pnode != NULL)
    {
      if(pnode->isfree == HEAP_TRUE)
      {
        if(pnode->size >= size)
        {
          break;
        }
      }
      pnode = pnode->pnext;
    }
    // 找到了符合条件的内存块就进行分配,否则就直接返回NULL
    if(pnode != NULL)
    {
      // 找到的内存块大于所需内存,就需要进行拆分,因为每个内存块都需要一个内存管理结构体,所以
      // 如果拆分后另一个内存块大小大于一个结构体大小,则拆分出一个新的空闲内存块,并插入链表,
      // 如果拆分后另一个内存块大小小于一个结构体大小,无法作为一个新内存块使用,那就不拆分。
      if(pnode->size > (size + sizeof(sHeapList_t)))
      {
        unsigned char *start = pnode->penter + size;
        sHeapList_t *newnode = (sHeapList_t *)start;
        // 初始化新内存块
        newnode->plast = pnode;
        newnode->pnext = pnode->pnext;
        newnode->isfree = HEAP_TRUE;
        newnode->penter = start + sizeof(sHeapList_t);
        newnode->size = pnode->size - (size + sizeof(sHeapList_t));
        // 初始化用户申请的内存块
        pnode->size = size;
        pnode->pnext = newnode;
        pnode->isfree = HEAP_FALSE;
      }
      else
      {
        // 找到无法拆分的内存块就直接给用户使用
        pnode->isfree = HEAP_FALSE;
      }
      return pnode->penter; // 返回用户内存入口地址
    }
  }
  return NULL;
}
// 内存释放
void pfree(void *p)
{
  sHeapList_t *pnode = (sHeapList_t *)((unsigned char *)p - sizeof(sHeapList_t));
  if(pnode == NULL)
  {
    return;
  }
  // 因为内存块都是按地址顺序排列在链表中的(空闲内存块和用户内存块在同一个链表),
  // 因此释放时只需合并内存块就可以了
  if(pnode->pnext != NULL)
  {
    // 当下一个节点存在时,且是空闲内存块
    if(pnode->pnext->isfree == HEAP_TRUE)
    {
      // 当前节点和下一个节点相邻,可以合并
      pnode->size += pnode->pnext->size + sizeof(sHeapList_t);
      pnode->pnext = pnode->pnext->pnext;
    }
  }
  if(pnode->plast != NULL)
  {
    // 当上一个节点存在时,且是空闲内存块
    if(pnode->plast->isfree == HEAP_TRUE)
    {
      // 当前节点和上一个节点相邻,可以合并
      pnode->plast->size += pnode->size + sizeof(sHeapList_t);
      pnode->plast->pnext = pnode->pnext;
    }
  }
  pnode->isfree = HEAP_TRUE; // 不管内存块是否合并成功,最后都要将内存块设置为空闲状态
}
 
heap.h
#ifndef __HEAP_H
#define __HEAP_H
// Define NULL pointer value
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
// 模块功能:单片机内存动态管理,支持内存碎片回收
// 动态管理的内存大小,单位:字节
#define HEAP_TOTAL_SIZE (10 * 1024) // 10KB
// 内存申请(注:函数不可重入)
void *pmalloc(unsigned int size);
// 内存释放(带有内存碎片回收机制)
void pfree(void *p);
 
#endif
 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!