C语言malloc申请内存时的碎片问题

嵌入式技术

1368人已加入

描述

解决问题:malloc在申请内存的时候,内存碎片问题会导致原本内存大小足够,却申请大内存失败;

比如:原本内存还有10M内存,此时先申请4M内存,再申请16Bytes内存,之后把4M内存释放掉,按理来说,此时应该还有 10M - 16Bytes 内存,但此时,再去申请8M的大内存,则申请失败。

因为malloc申请的内存,必须是一块连续的内存,但此时中间已经有16Bytes内存碎片导致内存不连续,所以申请内存失败;

内存

以下是我针对碎片问题,对内存管理机制做出一种优化方案:在开机初始化内存之后,先申请一块1M左右内存(根据情况修改大小),用作内存碎片管理,然后把这1M内存分为很多个小内存,并把小内存的地址放在链接节点中,之后申请内存时,优先判断内存碎片管理中是否有满足大小的小内存。

有的话,直接使用提前申请的小内存就可以了,如果内存管理机制中没有适合的内存,但重新用malloc()函数申请;

接下来,解释我写的碎片管理机制:

1 mm_management_init()初始化

 


void mm_management_init(unsigned int free_memory_start, unsigned int free_memory_end)

 

传入参数free_memory_start是内存初始化之后,剩余可申请的首地址,该地址,一般会传入到main函数,如果main()函数没有传入该参数的话,可以在内存初始化之后,自己malloc(4)申请一下,把返回的地址作为mm_management_init()函数的第一个参数;

传入参数free_memory_end是可以申请的最大地址,每个IC各有不同;

mm_management_init()对16bytes,64bytes,256bytes,512bytes,1024bytes,4096bytes这些小内存做优化,提前计算小内存占用的总大小。

然后直接申请这块大内存占住,再把这块大内存分配给各个小内存,并记录在链表中,比如:mm_fix_16_head

2 mm_management_malloc()申请函

 

unsigned int  mm_management_malloc(unsigned int size)
   
 申请内存的时候,先判断size大小,如果大小可以在内存管理机制中找到,则直接返回提前申请地址,如果大小不满足,或者小内存已被申请完,则用malloc重新申请。    
 在内存管理机制中拿到的小内存,该链表节点的标记会设为MM_STATUS_BUSY。

 


3 mm_management_free()

 

void mm_management_free(void *mm_ptr)
  
  与mm_management_malloc()相反,先检查所有小内存链表是都有该地址,有的话就把该地址内存清0,并把标记设为MM_STATUS_FREE;如果是用malloc申请的,当时是free()释放掉;

 

 
   接下来是代码:

 

#include
#include


#define C_MM_16BYTE_NUM    (32)
#define C_MM_64BYTE_NUM    (16)
#define C_MM_256BYTE_NUM   (12)
#define C_MM_512BYTE_NUM   (12)
#define C_MM_1024BYTE_NUM   (18)
#define C_MM_4096BYTE_NUM   (30)


#define C_MM_16BYTE     (16)
#define C_MM_64BYTE     (64)
#define C_MM_256BYTE    (256)
#define C_MM_512BYTE    (512)
#define C_MM_1024BYTE    (1024)
#define C_MM_4096BYTE    (4096)


#define C_MM_MAX_SIZE    C_MM_4096BYTE //碎片管理最大的碎片大小


#define MM_STATUS_FREE    (0)   //0:表示内存空闲
#define MM_STATUS_BUSY    (1)   //1:表示内存已被申请


#define MM_STATUS_OK                (0)
#define MM_STATUS_FAIL              (1)


typedef struct mm_node_struct {
 unsigned int    *mm_node;   //存放内存节点指针
 unsigned short   iflag;    //指针是否空闲
 struct P_MM_Node_STRUCT *next;    //指向下一个内存节点指针
} MM_Node_STRUCT, *P_MM_Node_STRUCT;


typedef struct mm_sdram_struct {
 unsigned int    count;
 P_MM_Node_STRUCT  *next;
} MM_SDRAM_STRUCT, *P_MM_SDRAM_STRUCT;


static MM_SDRAM_STRUCT mm_fix_16_head;
static MM_SDRAM_STRUCT mm_fix_64_head;
static MM_SDRAM_STRUCT mm_fix_256_head;
static MM_SDRAM_STRUCT mm_fix_512_head;
static MM_SDRAM_STRUCT mm_fix_1024_head;
static MM_SDRAM_STRUCT mm_fix_4096_head;


static P_MM_SDRAM_STRUCT pmm_fix_16_head = &mm_fix_16_head;
static P_MM_SDRAM_STRUCT pmm_fix_64_head = &mm_fix_64_head;
static P_MM_SDRAM_STRUCT pmm_fix_256_head = &mm_fix_256_head;
static P_MM_SDRAM_STRUCT pmm_fix_512_head = &mm_fix_512_head;
static P_MM_SDRAM_STRUCT pmm_fix_1024_head = &mm_fix_1024_head;
static P_MM_SDRAM_STRUCT pmm_fix_4096_head = &mm_fix_4096_head;


static P_MM_Node_STRUCT mm_management_getnode(P_MM_SDRAM_STRUCT pmm_fix_head);
static unsigned int mm_management_node_free(P_MM_SDRAM_STRUCT pmm_fix_head, unsigned int *mm_ptr, unsigned int size);


static unsigned int *mm_management_ptr = NULL;
static unsigned int mm_management_size = 0;


/*
** free_memory_start : 开机内存初始化之后,剩余可以申请的地址的首地址
** free_memory_end   : 内存可以申请的最大地址
*/
void mm_management_init(unsigned int free_memory_start, unsigned int free_memory_end)
{
 unsigned int mm_usesize=0,offset=0,mm_offset;
 unsigned char *ptr_tmp;
 unsigned int i;
 P_MM_Node_STRUCT pmm_fix_head, pmm_fix_tmp;


 free_memory_start = (free_memory_start + 3) & (~0x3);  // Align to 4-bytes boundary
 free_memory_end   = (free_memory_end + 3) & (~0x3);   // Align to 4-bytes boundary


 do{
  //[1]判断剩余内存是否满足碎片管理所需大小
  mm_usesize = 0;
  mm_usesize += C_MM_16BYTE * C_MM_16BYTE_NUM;
  mm_usesize += C_MM_64BYTE * C_MM_64BYTE_NUM;
  mm_usesize += C_MM_256BYTE * C_MM_256BYTE_NUM;
  mm_usesize += C_MM_512BYTE * C_MM_512BYTE_NUM;
  mm_usesize += C_MM_1024BYTE * C_MM_1024BYTE_NUM;
  mm_usesize += C_MM_4096BYTE * C_MM_4096BYTE_NUM;


  if(mm_usesize+free_memory_start > free_memory_end)
  {
   printf("free memory not enough for mm management,init fail
");
   break;
  }
  mm_management_ptr = (unsigned char *)malloc(mm_usesize);    //申请整块碎片管理内存大小  //如果有malloc_align函数,建议改用malloc_align申请64bit对其的内存
  if(mm_management_ptr == NULL)
  {
   printf("mm management malloc fail,init fail
");
   break;
  }
  mm_management_size = mm_usesize;
  ptr_tmp = mm_management_ptr;
  memset(ptr_tmp, 0x00, mm_usesize);


  //[2]内存链表头初始化,用于存放以下步骤的子链表节点
  memset((void*)pmm_fix_16_head, 0x00, sizeof(mm_fix_16_head));
  memset((void*)pmm_fix_64_head, 0x00, sizeof(mm_fix_64_head));
  memset((void*)pmm_fix_256_head, 0x00, sizeof(mm_fix_256_head));
  memset((void*)pmm_fix_512_head, 0x00, sizeof(mm_fix_512_head));
  memset((void*)pmm_fix_1024_head, 0x00, sizeof(mm_fix_1024_head));
  memset((void*)pmm_fix_4096_head, 0x00, sizeof(mm_fix_4096_head));  
  
  //[3]申请16Bytes碎片内存存放在链表  
  mm_offset = 0;
  mm_fix_16_head.count = C_MM_16BYTE_NUM;
  pmm_fix_head = pmm_fix_16_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_16BYTE * i) + mm_offset;    //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }


  //[4]申请64Bytes碎片内存存放在链表  
  mm_offset += C_MM_16BYTE * C_MM_16BYTE_NUM;
  mm_fix_64_head.count = C_MM_64BYTE_NUM;
  pmm_fix_head = pmm_fix_64_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_64BYTE * i) + mm_offset;    //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }


  //[5]申请256Bytes碎片内存存放在链表  
  mm_offset += C_MM_64BYTE * C_MM_64BYTE_NUM;
  mm_fix_256_head.count = C_MM_256BYTE_NUM;
  pmm_fix_head = pmm_fix_256_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_256BYTE * i) + mm_offset;   //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }


  //[6]申请512Bytes碎片内存存放在链表  
  mm_offset += C_MM_256BYTE * C_MM_256BYTE_NUM;
  mm_fix_512_head.count = C_MM_512BYTE_NUM;
  pmm_fix_head = pmm_fix_512_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_512BYTE * i) + mm_offset;   //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }


  //[7]申请1024Bytes碎片内存存放在链表  
  mm_offset += C_MM_512BYTE * C_MM_512BYTE_NUM;
  mm_fix_1024_head.count = C_MM_1024BYTE_NUM;
  pmm_fix_head = pmm_fix_1024_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_1024BYTE * i) + mm_offset;   //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }
  
  //[8]申请4096Bytes碎片内存存放在链表  
  mm_offset += C_MM_1024BYTE * C_MM_1024BYTE_NUM;
  mm_fix_4096_head.count = C_MM_4096BYTE_NUM;
  pmm_fix_head = pmm_fix_4096_head;
  for(i=0; iiflag = MM_STATUS_FREE;
   pmm_fix_tmp->next = NULL;
   offset = (C_MM_4096BYTE * i) + mm_offset;   //计算小内存碎片在大buf里的偏移地址
   pmm_fix_tmp->mm_node = ptr_tmp + offset;
   
   pmm_fix_head->next = pmm_fix_tmp;
   pmm_fix_head = pmm_fix_tmp;
  }
 }while(0);
 
 printf("mm management init end!!!
");
}


unsigned int  mm_management_malloc(unsigned int size)
{
 int status = MM_STATUS_FAIL;   //MM_STATUS_FAIL表示还没申请到碎片内存
 P_MM_Node_STRUCT  pmm_fix_node;
 unsigned int *mm_ptr = NULL;


 //获取空闲碎片节点
 do{


  //[1]判断申请内存大小是否满足要求
  if(size < 0)
  {
   status = MM_STATUS_FAIL;
   printf("mm management malloc size is error
");
   return NULL;
  }


  //[2]判断大小是否小于16Byets
  if(size < C_MM_16BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_16_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }


  //[3]判断大小是否小于64Byets
  if(size < C_MM_64BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_64_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }


  //[4]判断大小是否小于256Byets
  if(size < C_MM_256BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_256_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }


  //[5]判断大小是否小于512Byets
  if(size < C_MM_512BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_512_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }


  //[6]判断大小是否小于1024Byets
  if(size < C_MM_1024BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_1024_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }


  //[7]判断大小是否小于4096Byets
  if(size < C_MM_4096BYTE && status == MM_STATUS_FAIL)
  {
   pmm_fix_node = mm_management_getnode(pmm_fix_4096_head);
   if(pmm_fix_node != NULL)
   {
    status = MM_STATUS_OK;
    break;
   }
  }
 }while(0);


 if(status == MM_STATUS_OK)


 {
  mm_ptr = pmm_fix_node->mm_node;
  pmm_fix_node->iflag = MM_STATUS_BUSY;
 }
 else
 {
  mm_ptr = (unsigned int *)malloc(size);
 }


 return (unsigned int *)mm_ptr;
}


void mm_management_free(void *mm_ptr)
{
 unsigned int i;
 int status = MM_STATUS_FAIL;
 P_MM_Node_STRUCT  pmm_fix_node;


 do{
  //[1]如果地址是16Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_16_head, mm_ptr, C_MM_16BYTE);
  if(status == MM_STATUS_OK)
   break;


  //[2]如果地址是64Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_64_head, mm_ptr, C_MM_64BYTE);
  if(status == MM_STATUS_OK)
   break;


  //[1]如果地址是256Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_256_head, mm_ptr, C_MM_256BYTE);
  if(status == MM_STATUS_OK)
   break;


  //[1]如果地址是512Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_512_head, mm_ptr, C_MM_512BYTE);
  if(status == MM_STATUS_OK)
   break;


  //[1]如果地址是1024Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_1024_head, mm_ptr, C_MM_1024BYTE);
  if(status == MM_STATUS_OK)
   break;


  //[1]如果地址是4096Bytes碎片地址,则释放内存
  status = mm_management_node_free(pmm_fix_4096_head, mm_ptr, C_MM_4096BYTE);
  if(status == MM_STATUS_OK)
   break;
 }while(0);


 if(status == MM_STATUS_OK)
 {
  //do nothing,在mm_management_node_free函数中已经将pmm_fix_node->iflag设为MM_STATUS_FREE
 }
 else
 {
  free(mm_ptr);
 }
}


//获取MM_SDRAM_STRUCT里的空闲节点
static P_MM_Node_STRUCT mm_management_getnode(P_MM_SDRAM_STRUCT pmm_fix_head)
{
 P_MM_SDRAM_STRUCT pmm_fix_head_tmp = pmm_fix_head;
 P_MM_Node_STRUCT  pmm_fix_node = pmm_fix_head_tmp->next;
 unsigned int count = pmm_fix_head_tmp->count;
 unsigned int i;


 for(i=0; iiflag == MM_STATUS_FREE)
   break;
  pmm_fix_node = pmm_fix_node->next;
 }


 if(i < count)
  return pmm_fix_node;
 else
  return NULL;
}


//比较MM_SDRAM_STRUCT的所有节点,如果地址一致,则释放地址
static unsigned int mm_management_node_free(P_MM_SDRAM_STRUCT pmm_fix_head, unsigned int *mm_ptr, unsigned int size)
{
 P_MM_SDRAM_STRUCT pmm_fix_head_tmp = pmm_fix_head;
 P_MM_Node_STRUCT  pmm_fix_node = pmm_fix_head_tmp->next;
 unsigned int count = pmm_fix_head_tmp->count;
 unsigned int i;


 for(i=0; imm_node == mm_ptr)
  {
   if(pmm_fix_node->iflag == MM_STATUS_FREE)
   {
    printf("mm management have been free
");
   }
   else
   {
    pmm_fix_node->iflag = MM_STATUS_FREE;
    memset((void *)mm_ptr, 0x00, size);    //释放内存后,把碎片内存清0
   }
   
   return MM_STATUS_OK;
  }
  pmm_fix_node = pmm_fix_node->next;
 }


 return MM_STATUS_FAIL;
}
  这份代码我写得还是比较简单,注释些也写得清楚,明白它的原理,应该很容易就看懂。

 

说一下这个机制的优缺点:

优点:

小内存申请的时候,先去提前申请好的内存中获取,这样可以很好地解决内存碎片问题。

缺点以及优化:

1.碎片管理机制可申请的碎片数量是有限的,当数量被申请完之后,还是得重新用malloc申请;但是这可以通过我定义的 C_MM_16BYTE_NUM 和 C_MM_16BYTE 这些宏定义修改碎片数量,根据项目需要修改数量,也是能很好的优化此问题;

2.比如我要申请4个Bytes,但此时,16,64,256,512,1024这几个链表已经用完了,那此时它会用4096这个链表去给4Bytes使用,当然,这同样可以修改C_MM_16BYTE_NUM 和 C_MM_16BYTE 这些宏定义优化这个问题。

  审核编辑:汤梓红

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分