自写单片机malloc,高效利用ram,不再有内存碎片

本文介绍了一种针对单片机应用的内存管理方法,通过自定义内存分配算法避免内存碎片,提高内存利用率。该算法在分配内存时采用固定大小的堆空间,利用时间换空间的策略,在释放内存时重新组织堆空间,确保后续内存分配的连续性。
该文章已生成可运行项目,

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

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

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

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

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

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

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

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

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

#include "mem_malloc.h"

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

#define DEBUG_EN        0
#define MEM_START       &mem[0]
#define MEM_END         &mem[MEM_SIZE]  
#define BLK_SIZE        sizeof(mem_block) 

void print_mem_info(void){
        printf("------------mem_info--------------\n");
        printf("sizeof(mem_block)=%d\n", BLK_SIZE);
        printf("MEM_START = %d(0x%x)\n", (int)MEM_START, (int)MEM_START);
        printf("MEM_END   = %d(0x%x)\n", (int)MEM_END, (int)MEM_END);
        printf("MEM_SIZE  = %d(0x%x)\n", (int)MEM_SIZE, (int)MEM_SIZE);
        printf("----------------------------------\n");
}

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

void print_mem_hex(int size){
        print_hex(mem, size);
}

int mem_malloc(unsigned int msize){
        unsigned int all_size = msize + sizeof(mem_block);
        mem_block tmp_blk;
        if(msize == 0) return 0;
        if(sum){
                mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*(sum-1));
                int free_blk = (char *)ptr_blk->mem_ptr-(MEM_START + BLK_SIZE*sum);
                if(all_size <= free_blk){
                        tmp_blk.mem_ptr = ptr_blk->mem_ptr - msize;
                        tmp_blk.mem_size = msize;
                        tmp_blk.mem_index = ptr_blk->mem_index + 1;
                        memcpy(MEM_START + BLK_SIZE*sum, &tmp_blk, BLK_SIZE);
                        sum = sum + 1;
                #if DEBUG_EN
                        printf("mem_ptr = 0x%x\n", (int)tmp_blk.mem_ptr);
                        printf("mem_size = 0x%x\n", tmp_blk.mem_size);
                        printf("mem_index = 0x%x\n", tmp_blk.mem_index);
                #endif
                        return tmp_blk.mem_index;
                }
        }else{
                if(all_size <= MEM_SIZE){
                        tmp_blk.mem_ptr = MEM_END - msize;
                        tmp_blk.mem_size = msize;
                        tmp_blk.mem_index = 1;
                        memcpy(MEM_START, &tmp_blk, BLK_SIZE);
                        sum = 1;
        #if DEBUG_EN
                        printf("mem_ptr = 0x%x\n", (int)tmp_blk.mem_ptr);
                        printf("mem_size = 0x%x\n", tmp_blk.mem_size);
                        printf("mem_index = 0x%x\n", tmp_blk.mem_index);
        #endif
                        return 1;
                }
        }
        return 0;
}

void *mem_buffer(int id){
        for(int i=0; i<sum; i++){
                mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*i);
                if(id == ptr_blk->mem_index){
                        return ptr_blk->mem_ptr;
                }
        }
        return NULL;
}

void mem_free(int id){
        for(int i=0; i<sum; i++){
                mem_block *ptr_blk = (mem_block *)(MEM_START + BLK_SIZE*i);
                if(id == ptr_blk->mem_index){
                        mem_block *ptr_old;
                        if(i != (sum-1)){
                                int offset = ptr_blk->mem_size;
                                int move_size = 0; 
                                int n = sum - i;
                                mem_block *ptr_tmp;
                                for(int j=1; j<n; j++){
                                        ptr_tmp = (mem_block *)(MEM_START + BLK_SIZE*(i+j));
                                        move_size += ptr_tmp->mem_size;
                                }
                                //memmove();
                                char *dst_addr = ptr_tmp->mem_ptr + move_size + offset - 1;
                                char *src_addr = ptr_tmp->mem_ptr + move_size - 1;
                                for(int j=move_size; j>0; j--){
                                        *dst_addr-- = *src_addr--;
                                }
                                int len = dst_addr - src_addr + 1;
                                memset(src_addr, 0, len);
                                for(int j=0; j<(n-1); j++){
                                        ptr_tmp = (mem_block *)(MEM_START + BLK_SIZE*(i+j));
                                        ptr_old = (mem_block *)(MEM_START + BLK_SIZE*(i+j+1));
                                        memcpy(ptr_tmp, ptr_old, BLK_SIZE);
                                        ptr_tmp->mem_ptr += offset;
                                }
                        }else{
                                ptr_old = (mem_block *)(MEM_START + BLK_SIZE*i);
                                memset(ptr_old->mem_ptr, 0, ptr_old->mem_size);
                        }
                        memset(ptr_old, 0, BLK_SIZE);
                        sum = sum - 1;
                        break; 
                }
        }
}

main.c

#include "mem_malloc.h"

char mem_id[10]={0};

void test_malloc(int i, int size){
	printf("------test_malloc-------\n");
	mem_id[i] = mem_malloc(size);
	if(mem_id[i] == 0){
		printf("malloc --- fail\n");
		printf("size=%d\n", size);
	}else{
		char *p = mem_buffer(mem_id[i]);
        memset(p, i, size);
        printf("p = 0x%x, id=%d, size=%d\n", (int)p, mem_id[i], size);
	}
	print_mem_hex(MEM_SIZE);
}

void test_buffer(int i, int size){
	printf("------test_buffer-------\n");
	printf("id = %d, size=%d\n", mem_id[i], size);
	char *p = mem_buffer(mem_id[i]);
    if(p != NULL){
		memset(p, 0xf0+i, size);
        print_mem_hex(MEM_SIZE);
	}
}

void test_free(int id){
	printf("------test_free-------\n");
	printf("id = %d\n", mem_id[id]);
	mem_free(mem_id[id]);
	print_mem_hex( MEM_SIZE);
}

void main(void){

	print_mem_info();
	test_malloc(1, 10);
	test_malloc(2, 8);
	test_malloc(3, 20);
	test_free(2);
	test_malloc(4, 10);
	test_free(1);
	test_malloc(5, 20);
	test_malloc(6, 10);
	test_malloc(7, 10);
	test_free(6);
	test_malloc(8, 13);
	test_buffer(5, 20);
	test_free(4);
	test_buffer(3, 20);
	test_malloc(9, 15);
	test_malloc(10, 15);
} 

结果:

------------mem_info--------------
sizeof(mem_block)=12
MEM_START = 134525056(0x804b080)
MEM_END   = 134525184(0x804b100)
MEM_SIZE  = 128(0x80)    //方便演示以128字节空间测试
----------------------------------
------test_malloc-------
p = 0x804b0f6, id=1, size=10
f6 b0 04 08 0a 00 00 00 01 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 01 01 
01 01 01 01 01 01 01 01                       //第一次分配的10个字节空间

------test_malloc-------
p = 0x804b0ee, id=2, size=8
f6 b0 04 08 0a 00 00 00 01 00 00 00 
ee b0 04 08 08 00 00 00 02 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 02 02 02 02 02 02 02 02 01 01   // 第二次分配的8个字节空间
01 01 01 01 01 01 01 01 
------test_malloc-------
p = 0x804b0da, id=3, size=20
f6 b0 04 08 0a 00 00 00 01 00 00 00 
ee b0 04 08 08 00 00 00 02 00 00 00 
da b0 04 08 14 00 00 00 03 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 03 03 03 03   //第三次分配的20个字节空间
03 03
02 02 02 02 02 02 02 02 01 01 
01 01 01 01 01 01 01 01 
------test_free-------
id = 2
f6 b0 04 08 0a 00 00 00 01 00 00 00 
e2 b0 04 08 14 00 00 00 03 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 03 03 01 01  //释放掉了第二次分配的8字节空间
01 01 01 01 01 01 01 01 

------test_malloc-------
p = 0x804b0d8, id=4, size=10
f6 b0 04 08 0a 00 00 00 01 00 00 00 
e2 b0 04 08 14 00 00 00 03 00 00 00 
d8 b0 04 08 0a 00 00 00 04 00 00 00
 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 04 04 04 04 04 04 04 04  //第四次分配10字节空间
04 04
03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 03 03 01 01 
01 01 01 01 01 01 01 01 
------test_free-------
id = 1
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03                       //释放掉了第一次分配的10字节空间

------test_malloc-------
p = 0x804b0ce, id=5, size=20
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 05 05 05 05 05 05 
05 05 05 05 05 05 05 05 05 05 05 05  //第五次分配20字节空间
05 05
04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 
------test_malloc-------
p = 0x804b0c4, id=6, size=10
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 
c4 b0 04 08 0a 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 06 06 06 06   //第六次分配10字节空间
06 06 06 06 06 06
05 05 05 05 05 05 
05 05 05 05 05 05 05 05 05 05 05 05 
05 05 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 
------test_malloc-------
malloc --- fail
size=10
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 
c4 b0 04 08 0a 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 06 06 06 06   //剩余20字节-12字节=8字节,所以第七次分配10字节失败
06 06 06 06 06 06 05 05 05 05 05 05 
05 05 05 05 05 05 05 05 05 05 05 05 
05 05 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 

------test_free-------
id = 6
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 05 05 05 05 05 05   //释放掉了第六次分配的10字节空间
05 05 05 05 05 05 05 05 05 05 05 05 
05 05 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03
 
------test_malloc-------
p = 0x804b0c1, id=6, size=13
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 
c1 b0 04 08 0d 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 08 08 08 08 08 08 08   //第八次分配13字节空间
08 08 08 08 08 08
05 05 05 05 05 05 
05 05 05 05 05 05 05 05 05 05 05 05 
05 05 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 
------test_buffer-------
id = 5, size=20
ec b0 04 08 14 00 00 00 03 00 00 00 
e2 b0 04 08 0a 00 00 00 04 00 00 00 
ce b0 04 08 14 00 00 00 05 00 00 00 
c1 b0 04 08 0d 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 08 08 08 08 08 08 08 
08 08 08 08 08 08 f5 f5 f5 f5 f5 f5 
f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5        //修改第五次分配的空间内容

f5 f5 04 04 04 04 04 04 04 04 04 04 
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 
------test_free-------
id = 4
ec b0 04 08 14 00 00 00 03 00 00 00 
d8 b0 04 08 14 00 00 00 05 00 00 00 
cb b0 04 08 0d 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 08 08 08 08 08 08 08 08 08 
08 08 08 08 f5 f5 f5 f5 f5 f5 f5 f5 
f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5         //释放第四次分配的10字节空间
03 03 03 03 03 03 03 03 03 03 03 03 
03 03 03 03 03 03 03 03 

------test_buffer-------
id = 3, size=20
ec b0 04 08 14 00 00 00 03 00 00 00 
d8 b0 04 08 14 00 00 00 05 00 00 00 
cb b0 04 08 0d 00 00 00 06 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 08 08 08 08 08 08 08 08 08 
08 08 08 08 f5 f5 f5 f5 f5 f5 f5 f5 
f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 
f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3      //修改第三次分配的空间内容
f3 f3 f3 f3 f3 f3 f3 f3 

------test_malloc-------
p = 0x804b0bc, id=7, size=15
ec b0 04 08 14 00 00 00 03 00 00 00 
d8 b0 04 08 14 00 00 00 05 00 00 00 
cb b0 04 08 0d 00 00 00 06 00 00 00 
bc b0 04 08 0f 00 00 00 07 00 00 00
 
00 00 00 00 00 00 00 00 00 00 00 00 
09 09 09 09 09 09 09 09 09 09 09 09  //第九次分配15字节空间
09 09 09
08 08 08 08 08 08 08 08 08 
08 08 08 08 f5 f5 f5 f5 f5 f5 f5 f5 
f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 
f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 
f3 f3 f3 f3 f3 f3 f3 f3 
------test_malloc-------
malloc --- fail
size=15
ec b0 04 08 14 00 00 00 03 00 00 00 
d8 b0 04 08 14 00 00 00 05 00 00 00 
cb b0 04 08 0d 00 00 00 06 00 00 00 
bc b0 04 08 0f 00 00 00 07 00 00 00 

00 00 00 00 00 00 00 00 00 00 00 00 
09 09 09 09 09 09 09 09 09 09 09 09   //剩余12字节-12字节=0字节,所以第十次分配15字节失败
09 09 09 08 08 08 08 08 08 08 08 08 
08 08 08 08 f5 f5 f5 f5 f5 f5 f5 f5 
f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 
f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 
f3 f3 f3 f3 f3 f3 f3 f3 

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

本文章已经生成可运行项目
malloc是C语言中用于动态内存分配的核心函数,用于在程序运行时根据需求申请指定大小的内存块[^1]。 ### 原理 malloc采用brk和mmap两种方式结合的策略,是在内存分配效率、内存管理和系统资源利用之间取得的一种平衡。申请动态内存时,如果是第一次申请,会分配一定页数(如33页)的动态内存,然后划出一块内存(包含8字节动态内存块控制信息),返回用户一块可使用的内存(一般在控制信息块后面);若不是第一次申请,则从已有的动态内存块中查找空闲(已被free)且大小合适(不小于用户申请大小)的内存块返回给用户,并将其设置为忙碌状态;若当前没有合适的,就从后面的动态内存中划分一块合适的内存;若后面内存不足,则进行页映射(分配新的一页)。在free过程中,若发现多块动态内存处于空闲状态,会合并动态内存块;若发现页空闲(除最开始分配的页外),则回收页(取消页映射),目的是提高分配申请的效率[^3][^5]。 ### 使用方法 以下是一个使用malloc分配内存的示例代码: ```c #include <stdio.h> #include <stdlib.h> int main() { // 使用malloc分配一个能存储10个整数的空间 int *arr = (int *)malloc(10 * sizeof(int)); if (arr == NULL) { // 内存分配失败 printf("Memory allocation failed\n"); return 1; } // 填充动态数组 for (int i = 0; i < 10; i++) { arr[i] = i; } // 打印数组内容 for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("\n"); // 释放内存 free(arr); // 将指针置为NULL,防止后续误用 arr = NULL; return 0; } ``` 在上述代码中,首先使用`malloc(10 * sizeof(int))`分配了能存储10个整数的内存空间,返回的指针需要进行类型转换。然后检查指针是否为`NULL`,以判断内存分配是否成功。接着对分配的内存进行操作,最后使用`free`函数释放内存,并将指针置为`NULL`,防止悬挂指针的出现[^4]。 ### 注意事项 - **内存泄漏**:使用`malloc`分配的内存,在不再使用时必须使用`free`函数释放,否则会造成内存泄漏。例如,在循环中不断使用`malloc`分配内存而不释放,会导致可用内存越来越少,最终程序崩溃。 - **悬挂指针**:在使用`free`释放内存后,原指针会变成悬挂指针,若后续再使用该指针会导致未定义行为。因此,释放内存后应将指针置为`NULL`。 - **内存碎片**:动态内存在不断地申请和释放过程中,会产生内存碎片,导致部分内存永远无法被使用。为减少内存碎片,小内存可使用栈,大内存使用堆,并且及时释放不必要的动态内存。 - **内存分配失败**:`malloc`可能会因为系统内存不足等原因分配失败,此时会返回`NULL`,因此在使用分配的内存前,需要检查返回的指针是否为`NULL`。
评论 12
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值