赞
踩
在嵌入式应用开发中,常常会遇到RAM空间大小有限的情况,这时需要在使用有限空间的过程中精打细算,比如整块RAM的空间只有100bytes,现在需要划分一段空间去存储来自串口接收的数据,然而接收的这段数据长度是不固定的,范围在[10bytes,70bytes]之间,如果不使用动态内存分配,为了保证接收到所有数据,则会直接划分固定70bytes的空间去存储数据,这样当只收到10bytes的数据时,将会造成60bytes的空间浪费。在使用动态内存分配后,可以使得需要多少空间就划分多少空间。
C库中实现的malloc,free过于复杂,执行时间相对而言太长,不适合应用在嵌入式系统中。另外,库里的malloc不可重入,在多线程的情况下并不适用,在freertos的heap3.c中,使用库里的malloc函数前后都会挂起任务调度器。
#define CONFIG_TOTAL_HEAP_SIZE (30 * 1024)
static uint8_t heap_buf[CONFIG_TOTAL_HEAP_SIZE]; // 被管理的空间
typedef struct mem_heap {
struct mem_heap * volatile next; // 指向下一个memory block(data + free)
volatile uint32_t data_len; // length of data block(memory block中被user使用的空间长度)
} mem_heap_t;
typedef struct {
uint32_t base[MEM_MAX];
uint32_t end[MEM_MAX];
} mem_env_t; // MEM_MAX即可供管理的内存空间的数量,base,end分别为对应空间的起始及终止地址
/* 将自己定义的大buffer注册,使得用前方的数据结构管理该空间 */ void mem_register(mem_type_t mem_type, __ALIGN4 void *pool, __ALIGN4 uint32_t size) { mem_heap_t *mem_p; ASSERT(mem_type < MEM_MAX); ASSERT(IS_ALIGN4(pool)); ASSERT(IS_ALIGN4(size)); if (pool != NULL) { ASSERT(size > sizeof(mem_heap_t)); mem_p = (mem_heap_t *)pool; /* start of list */ mem_p->next = (mem_heap_t *)((uint32_t)pool + size - sizeof(mem_heap_t)); mem_p->data_len = 0U; /* end of list */ mem_p->next->next = NULL; mem_p->next->data_len = 0U; } mem_env.base[mem_type] = (uint32_t)pool; mem_env.end[mem_type] = (uint32_t)pool + size; } /* 申请内存 */ void *mem_malloc(mem_type_t mem_type, uint32_t wanted_size) { void *p_return = NULL; mem_heap_t *p_iterator, *p_new; uint32_t free_size, pool; ASSERT(mem_type < MEM_MAX); pool = mem_env.base[mem_type]; if (!pool) { return NULL; } p_iterator = (mem_heap_t *)pool; /* add header offset, make sure */ wanted_size += sizeof(mem_heap_t); wanted_size = ALIGN4_HI(wanted_size); ENTER_CRITICAL(); while (1) { free_size = (uint32_t)p_iterator->next - (uint32_t)p_iterator - p_iterator->data_len; /* check if free size is big enough */ if (free_size >= wanted_size) { break; } p_iterator = p_iterator->next; if (p_iterator->next == NULL) { /* failed, we are at the end of the list */ return NULL; } } if (p_iterator->data_len == 0U) { /* no block is allocated, set the length of the first element */ p_iterator->data_len = wanted_size; p_return = (void *)((uint32_t)p_iterator + sizeof(mem_heap_t)); } else { p_new = (mem_heap_t *)((uint32_t)p_iterator + p_iterator->data_len); p_new->next = p_iterator->next; p_new->data_len = wanted_size; p_iterator->next = p_new; p_return = (void *)((uint32_t)p_new + sizeof(mem_heap_t)); } EXIT_CRITICAL(); return p_return; } /* 释放内存 */ void mem_free(mem_type_t mem_type, void *mem_addr) { uint32_t pool; mem_heap_t *p_iterator, *p_previous, *p_return; ASSERT(mem_type < MEM_MAX); ASSERT(((uint32_t)mem_addr > mem_env.base[mem_type]) && ((uint32_t)mem_addr < mem_env.end[mem_type])); pool = mem_env.base[mem_type]; ASSERT(pool != 0); ENTER_CRITICAL(); p_return = (mem_heap_t *)((uint32_t)mem_addr - sizeof(mem_heap_t)); /* find block for mem_addr*/ p_previous = NULL; p_iterator = (mem_heap_t *)pool; while (p_iterator != p_return) { p_previous = p_iterator; p_iterator = p_iterator->next; if (p_iterator == NULL) { /* invalid mem_addr */ goto _exit; } } if (p_previous == NULL) { /* first block to be released, only set length to 0 */ p_iterator->data_len = 0U; } else { /* discard block from chain list */ p_previous->next = p_iterator->next; } _exit: EXIT_CRITICAL(); }
提示:在以下动图中:
- 黄色区域表示链表项,类型为mem_heap_t,用于串联前后两个memory block。
- 绿色区域表示用于存放数据的空间,即用户使用malloc返回的空间
- 白色区域表示free的空间,即未被使用的区域
接下来通过一部分代码及对应动图直观的体会下。
mem_register(MEM_GENERAL, heap_buf, CONFIG_TOTAL_HEAP_SIZE); // 注册自己定义的大数组作为heap,并初始化首位节点
uint32_t *p1 = mem_malloc(MEM_GENERAL, 5);
uint32_t *p2 = mem_malloc(MEM_GENERAL, 4);
uint32_t *p3 = mem_malloc(MEM_GENERAL, 5);
mem_free(MEM_GENERAL, p2);
mem_free(MEM_GENERAL, p1);
mem_free(MEM_GENERAL, p3);
本文介绍的内存分配的方法的一个亮点在于,当释放地址时,只需要将该地址的节点去掉就行,相比freertos的实现方法简单了不少。结合前方的代码以及动图演示,各位应该都能比较容易理解,over!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。