C语言malloc申请内存时的碎片问题
单片机与嵌入式 2024-08-07
解决问题: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\r\n"); 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\r\n"); 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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; i { pmm_fix_tmp = (P_MM_Node_STRUCT)malloc(sizeof(MM_Node_STRUCT)); pmm_fix_tmp->iflag = 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!!!\r\n");} 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\r\n"); 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; i { if(pmm_fix_node->iflag == 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; i { if(pmm_fix_node->mm_node == mm_ptr) { if(pmm_fix_node->iflag == MM_STATUS_FREE) { printf("mm management have been free\r\n"); } 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 这些宏定义优化这个问题。链接:https://blog.csdn.net/weixin_37981492/


声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 【7.24 深圳】2025国际AI+IoT生态发展大会/2025全球 MCU及嵌入式技术论坛


  • 相关技术文库
  • C语言
  • 编程
  • 软件开发
  • 程序
  • 51单片机数码管显示跑马灯程序源代码讲解

    基于51单片机学习板。用S1键作为控制跑马灯的方向按键,S5键作为控制跑马灯方向的加速度按键,S9键作为控制跑马灯方向的减速度按键,S13键作为控制跑马灯方向的启动或者暂停按键。记得把输出线P0.4一直输出低电平,...

    07-11
  • 基于ARM的智能家居控制通信控制站的设计与实现

    0 引言 物体信息化是现代社会信息化建设的“催化剂”和“增倍器”。只有走集成整合信息技术以及信息数据之路,企业的信息化建设才能真正发挥作用,才能进一步推动信息建设上水平。现代物体信息化的发展,直接刺激了新生...

    07-10
  • ARM中打印函数print 的几种实现方法

    1利用C库函数printf。 步骤: 1)首先需要包含头文件stdio.h。 2)然后定义文件句柄。实际上就是一个int型变量封装在结构体中。 struct__FILE{inthandle;}; 3)定义FILE__stdout;FILE即为__FILE,通过stdio.h宏定义。...

    07-10
  • 高效的C编程之:C编译器及其优化(上)

    本章将帮助读者在ARM处理器上编写高效的C代码。本章涉及的一些技术不仅适用于ARM处理器,也适用于其他RISC处理器。本章首先从ARM编译器及其优化入手,讲解C编译器在优化代码时所碰到的一些问题。理解这些问题,将有...

    07-08
  • 有关C51的编程规范

    简介:编程首要是要考虑程序的可行性,然后是可读性、可移植性、健壮性以及可测试性。这是总则。但是很多人忽略了可读性、可移植性和健壮性(可调试的方法可能歌不相同),这是不对的。 下面就来说说有关C51的编程规...

    07-08
  • 光立方程序编写步骤

    基于51单片机的4*4*4光立方程序实现原理及程序代码。LED光立方的复位电路、时钟电路、每层LED灯电路控制逻辑,系统总原理图,工作流程及相关C语言源码实现。希望能够对你学习了解LED光立方程序编写及LED立方实体制...

    07-04
  • 封装继承多态

    封装: 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型。 封装的意义: 封装的意义在于保护或者防止代码(数据)被我们无意中...

    07-04
  • 封装是什么意思?

    即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中...

    07-04
  • 超声波模块测距51程序_单片机超声波测距c语言

    超声波检测原理 超声波测距的程序流程图 程序如下: //超声波模块程序 //超声波模块程序 //Trig = P2^0 //Echo = P3^2 #include #define uchar unsigned char #define uint unsigned int // void delay(uint z) {...

    07-01
  • 大佬带你看嵌入式系统,嵌入式系统该学习什么?

    嵌入式系统是当今的热门系统之一,在诸多领域,嵌入式系统都有所应用。为增进大家对嵌入式系统的认识,小编将为大家介绍嵌入式系统是一个什么样的专业,以及学习嵌入式系统该学习哪些内容。如果你对嵌入式系统具有...

    06-27
  • c51单片机编程要点总结

    c51单片机编程要点总结 1、头文件:#include (我用的是 STC 89C54RD+) 2、预定义:sbit LED = P1^0// 定义 P1 口的 0 位为 LED 注:“P1^0”这个写法,与 A51 不同(A51 是 P1.0),P1 是一组端口,端口号范围 0~7 注2...

    06-25
下载排行榜
更多
评测报告
更多
广告