[示例代码] 单片机malloc,高效利用ram,不再有内存碎片

[复制链接]
2656|15
 楼主| hudi008 发表于 2022-10-26 21:54 | 显示全部楼层 |阅读模式

单片机应用中,malloc/free产生内存碎片的原因:

70.jpg

标准内存动态分配是动态链表进行管理。由于malloc返回的是一个指针再加上单片机没有mmu,使得分配的指针就像一个个钉子钉在内存中了。这就导致内存管理非常困难,从而产生我们常说的内存碎片。

我们来举一个极端的例子,导致大量内存碎片:

1. 单片机的RAM为1Kbyte,为了说明和计算方便我们忽略掉链表占用的空间,只计算实际存储空间大小。

2. 申请64块内存空间,每块是16字节,那么就会分配完1k字节的空间。即:
for(int i=0; i<64; i++){
    ptr = malloc(16);
}

3. 然后释放掉偶数块内存空间,即:
for(int i=0; i<64; i+=2){
    free(ptr );
}

4. 于是我们释放掉了一半的RAM空间,即有512字节的空间,但是都是非连续的。32块16字节的非连续空间,所以要分配出大于16字节的内存块是分配不出来的。有512字节的空间但只能分配小于16字节的连续空间(除非使用calloc分配非连续空间),在某些场合原本单片机RAM空间就很急,再加上这种不充分的使用使得程序稳定性大打折扣。

鉴于各中原因本人自己编写了一个内存管理,适合单片机使用的内存管理分配。

算法原理:
定义一个数组作为动态分配的堆空间,低地址空间保存管理数据,高地址空间实际分配给用户的缓存(类似堆栈使用,分配是往中间靠拢),free时移动高地址用户空间(以时间换空间),腾出多余未使用的空间,等待malloc来分配。

  1. #include "mem_malloc.h"

  2. static unsigned int sum = 0;
  3. static char                     mem[MEM_SIZE];

  4. #define DEBUG_EN        0
  5. #define MEM_START       &mem[0]
  6. #define MEM_END         &mem[MEM_SIZE]  
  7. #define BLK_SIZE        sizeof(mem_block)

  8. void print_mem_info(void){
  9.         printf("------------mem_info--------------\n");
  10.         printf("sizeof(mem_block)=%d\n", BLK_SIZE);
  11.         printf("MEM_START = %d(0x%x)\n", (int)MEM_START, (int)MEM_START);
  12.         printf("MEM_END   = %d(0x%x)\n", (int)MEM_END, (int)MEM_END);
  13.         printf("MEM_SIZE  = %d(0x%x)\n", (int)MEM_SIZE, (int)MEM_SIZE);
  14.         printf("----------------------------------\n");
  15. }

  16. void print_hex(char *data, int len){
  17.         for(int i=0; i<len; i++){
  18.                 printf("%02x ", (unsigned char)data[i]);
  19.                 if((i+1)%12 == 0)   printf("\n");
  20.         }
  21.         printf("\n");
  22. }

  23. void print_mem_hex(int size){
  24.         print_hex(mem, size);
  25. }

  26. int mem_malloc(unsigned int msize){
  27.         unsigned int all_size = msize + sizeof(mem_block);
  28.         mem_block tmp_blk;
  29.         if(msize == 0) return 0;
  30.         if(sum){
  31.                 mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*(sum-1));
  32.                 int free_blk = (char *)ptr_blk->mem_ptr-(MEM_START + BLK_SIZE*sum);
  33.                 if(all_size <= free_blk){
  34.                         tmp_blk.mem_ptr = ptr_blk->mem_ptr - msize;
  35.                         tmp_blk.mem_size = msize;
  36.                         tmp_blk.mem_index = ptr_blk->mem_index + 1;
  37.                         memcpy(MEM_START + BLK_SIZE*sum, &tmp_blk, BLK_SIZE);
  38.                         sum = sum + 1;
  39.                 #if DEBUG_EN
  40.                         printf("mem_ptr = 0x%x\n", (int)tmp_blk.mem_ptr);
  41.                         printf("mem_size = 0x%x\n", tmp_blk.mem_size);
  42.                         printf("mem_index = 0x%x\n", tmp_blk.mem_index);
  43.                 #endif
  44.                         return tmp_blk.mem_index;
  45.                 }
  46.         }else{
  47.                 if(all_size <= MEM_SIZE){
  48.                         tmp_blk.mem_ptr = MEM_END - msize;
  49.                         tmp_blk.mem_size = msize;
  50.                         tmp_blk.mem_index = 1;
  51.                         memcpy(MEM_START, &tmp_blk, BLK_SIZE);
  52.                         sum = 1;
  53.         #if DEBUG_EN
  54.                         printf("mem_ptr = 0x%x\n", (int)tmp_blk.mem_ptr);
  55.                         printf("mem_size = 0x%x\n", tmp_blk.mem_size);
  56.                         printf("mem_index = 0x%x\n", tmp_blk.mem_index);
  57.         #endif
  58.                         return 1;
  59.                 }
  60.         }
  61.         return 0;
  62. }

  63. void *mem_buffer(int id){
  64.         for(int i=0; i<sum; i++){
  65.                 mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*i);
  66.                 if(id == ptr_blk->mem_index){
  67.                         return ptr_blk->mem_ptr;
  68.                 }
  69.         }
  70.         return NULL;
  71. }

  72. void mem_free(int id){
  73.         for(int i=0; i<sum; i++){
  74.                 mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*i);
  75.                 if(id == ptr_blk->mem_index){
  76.                         mem_block *ptr_old;
  77.                         if(i != (sum-1)){
  78.                                 int offset = ptr_blk->mem_size;
  79.                                 int move_size = 0;
  80.                                 int n = sum - i;
  81.                                 mem_block *ptr_tmp;
  82.                                 for(int j=1; j<n; j++){
  83.                                         ptr_tmp = (mem_block *)(MEM_START + BLK_SIZE*(i+j));
  84.                                         move_size += ptr_tmp->mem_size;
  85.                                 }
  86.                                 //memmove();
  87.                                 char *dst_addr = ptr_tmp->mem_ptr + move_size + offset - 1;
  88.                                 char *src_addr = ptr_tmp->mem_ptr + move_size - 1;
  89.                                 for(int j=move_size; j>0; j--){
  90.                                         *dst_addr-- = *src_addr--;
  91.                                 }
  92.                                 int len = dst_addr - src_addr + 1;
  93.                                 memset(src_addr, 0, len);
  94.                                 for(int j=0; j<(n-1); j++){
  95.                                         ptr_tmp = (mem_block *)(MEM_START + BLK_SIZE*(i+j));
  96.                                         ptr_old = (mem_block *)(MEM_START + BLK_SIZE*(i+j+1));
  97.                                         memcpy(ptr_tmp, ptr_old, BLK_SIZE);
  98.                                         ptr_tmp->mem_ptr += offset;
  99.                                 }
  100.                         }else{
  101.                                 ptr_old = (mem_block *)(MEM_START + BLK_SIZE*i);
  102.                                 memset(ptr_old->mem_ptr, 0, ptr_old->mem_size);
  103.                         }
  104.                         memset(ptr_old, 0, BLK_SIZE);
  105.                         sum = sum - 1;
  106.                         break;
  107.                 }
  108.         }
  109. }

源码:https://github.com/chenqy2018/mem_malloc





lzbf 发表于 2022-11-1 09:38 | 显示全部楼层
c语言中malloc是什么?怎么用?
wwppd 发表于 2022-11-1 09:53 | 显示全部楼层
c++中malloc的含义与用法 具体点儿  
cemaj 发表于 2022-11-1 10:10 | 显示全部楼层
单片机malloc的size仅仅为申请内存字节大小,与申请内存块中存储的数据类型无关
sdlls 发表于 2022-11-1 10:35 | 显示全部楼层
如何自己编写malloc这个代码呢?
elsaflower 发表于 2022-11-1 11:10 | 显示全部楼层
malloc()之后,内核发生了什么?  
yeates333 发表于 2022-11-1 11:52 | 显示全部楼层
malloc 向系统申请分配指定size个字节的内存空间。
lzbf 发表于 2022-11-1 14:28 | 显示全部楼层
c语言中的malloc()包含在哪个库函数中
Henryko 发表于 2022-11-5 20:15 | 显示全部楼层
malloc 向系统申请分配指定size个字节的内存空间
tpgf 发表于 2022-11-7 10:30 | 显示全部楼层
malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。
heimaojingzhang 发表于 2022-11-7 10:41 | 显示全部楼层
malloc()用于单片机主要问题体现在容易产生内存碎片。
keaibukelian 发表于 2022-11-7 11:44 | 显示全部楼层
  内存碎片一般是由于空闲的连续空间比要申请的空间小,导致这些小内存块不能被利用.产生内存碎片的方法很简单,
  举个例:   
  假设有一块一共有100个单位的连续空闲内存空间,
  范围是0~99.如果你从中申请一块内存,如10个单位,
  那么申请出来的内存块就为0~9区间.这时继续申请一块内存,
  比如说5个单位大,第二块得到的内存块就应该为10~14区间.
  如果把第一块内存块释放,然后再申请一块大于10个单位的内存块,
  比如说20个单位.因为刚被释放的内存块不能满足新的请求,
  所以只能从15    开始分配出20个单位的内存块.现在整个内存空间的状态是0~9空闲,
  10~14被占用,15~24被占用,25~99空闲。
  其中0~9就是一个内存碎片了.如果10~14一直被占用,
  而以后申请的空间都大于10个单位,那么0~9就永远用不上了,
  造成内存浪费.
labasi 发表于 2022-11-7 11:55 | 显示全部楼层
本帖最后由 labasi 于 2022-11-7 13:28 编辑

和楼主同问
paotangsan 发表于 2022-11-7 12:08 | 显示全部楼层
alloc()函数其实就在内存中找一片指定大小的空间,然后将这个空间的首地址范围给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc()函数中参数size的具体内容。
renzheshengui 发表于 2022-11-7 13:06 | 显示全部楼层
malloc分配的内存空间在逻辑上连续的,而在物理上可以连续也可以不连续。
wakayi 发表于 2022-11-7 13:28 | 显示全部楼层
头文件:#include <malloc.h> 或 #include <alloc.h> (注意:alloc.h 与 malloc.h 的内容是完全一致的。)

功能:分配长度为num_bytes字节的内存块

说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

当内存不再使用时,应使用free()函数将内存块释放。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

253

主题

9898

帖子

11

粉丝
快速回复 在线客服 返回列表 返回顶部