以stm32f407为例,有时候想动态内存使用,就可以使用malloc函数来分配空间,使用完之后再使用free函数释放掉。
但是这样有一个缺点,stm32的内存资源相对来说是比较少的,而动态分配内存的大小取决于 .s 启动文件的 Heal_Size 的大小,一般来说,stm32f407 默认的堆大小是0x200,也就是512字节。当想要动态分配1k或者更大的空间时,就可以修改 Heal_Size 的大小,但是 Heal_Size 太大的话,会影响单片机的性能。
这时候就可以自己搞一个内存池,分配和释放自己管理,但是要注意使用的过程中不要越界
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#define MEMORY_POOL_SIZE 10240
static uint8_t g_memory_pool[MEMORY_POOL_SIZE];
typedef struct MemoryBlock_t {
int size; // 内存块的大小
uint8_t allocated; // 标识内存块是否已分配
}MemoryBlock_t;
typedef struct Memorylist_t
{
void* p_address;//存储分配空间的起始地址,通过该地址访问内存空间,注意不要越界
MemoryBlock_t* p_block; //存储空间的头
struct Memorylist_t* p_next;
struct Memorylist_t* p_prior;
}Memorylist_t;
Memorylist_t* p_rear_node = NULL;
// 初始化内存池
void initialize_g_memory_pool(void)
{
MemoryBlock_t* p_initial_block = (MemoryBlock_t*)g_memory_pool;
p_initial_block->size = MEMORY_POOL_SIZE - sizeof(MemoryBlock_t);
p_initial_block->allocated = 0;
}
// 分配内存
Memorylist_t* my_malloc(int size)
{
int offset = 0;
Memorylist_t* p_memorylist = (Memorylist_t*)malloc(sizeof(Memorylist_t));
while (offset < MEMORY_POOL_SIZE)
{
/* 顺着内存块的头找到空闲的内存块 */
MemoryBlock_t* p_current_block = (MemoryBlock_t*)(g_memory_pool + offset);
if ((!p_current_block->allocated) && (p_current_block->size >= size))//如果没有被分配,且内存大于size
{
if (p_current_block->size > (size + sizeof(MemoryBlock_t)))
{
/* 如果剩余空间可以用来放置一个新的内存块信息(头),则创建一个新的头
否则直接不创建新的头
这种方法有个缺点,当你创建了两个内存块,大小40和50,因为每个内存块带一个头
所以当释放第一个空间的时候,剩余空间40,当要创建大小为37的内存块的时候,
就只能创建size=37的空间,不能创建新的头,以至于下一次顺着头来寻找内存块的时候,
会卡在第一块无法向前
解决方法1:直接不在这40的空间里创建新的空间,而是到后边寻找更大的空间创建
解决方法2:就是这里使用的方法,当你释放空间之后,寻找释放的 块 之后的已经被分配了的块,向前移动
这样可以用一点点CPU资源换来更好的内存管理
*/
MemoryBlock_t* new_block = (MemoryBlock_t*)(g_memory_pool + offset + size + sizeof(MemoryBlock_t));
new_block->size = p_current_block->size - size - sizeof(MemoryBlock_t);
new_block->allocated = 0;
}
/* 更新当前 头 的信息 */
p_current_block->size = size;
p_current_block->allocated = 1;
/* 更新当前内存块的链表的信息 */
p_memorylist->p_block = p_current_block;
p_memorylist->p_address = (void*)((MemoryBlock_t*)p_current_block + 1);
p_memorylist->p_next = NULL;
if(p_rear_node == NULL)//第一个新的节点
{
p_memorylist->p_prior = NULL;
p_rear_node = p_memorylist;
}
else
{
p_memorylist->p_prior = p_rear_node;
p_rear_node->p_next = p_memorylist;
p_rear_node = p_memorylist;
}
return p_memorylist; // 返回分配的内存块的起始地址
}
offset += sizeof(MemoryBlock_t) + p_current_block->size;
}
free(p_memorylist);
return NULL; // 内存池中没有足够空间来分配所需大小的内存
}
// 释放内存
void my_free(Memorylist_t** ptr) {
if ((*ptr) != NULL) {
MemoryBlock_t* p_block_to_free = (MemoryBlock_t*)(*ptr)->p_address - 1; // 找到对应的内存块信息
Memorylist_t* p_temp_node = (*ptr);
//把节点从链表去除
if((*ptr)->p_prior == NULL)//头结点
{
p_rear_node = (*ptr)->p_next;
p_rear_node->p_prior = NULL;
}
if((*ptr)->p_next == NULL)//尾节点
{
p_rear_node = (*ptr)->p_prior;
p_rear_node->p_next = NULL;
}
else //身体节点
{
(*ptr)->p_next->p_prior = (*ptr)->p_prior;
(*ptr)->p_prior = (*ptr)->p_next;
}
p_block_to_free->allocated = 0;
// 合并相邻的空闲内存块
MemoryBlock_t* p_current_block = (MemoryBlock_t*)g_memory_pool;
while (p_current_block < ((MemoryBlock_t*)(g_memory_pool + MEMORY_POOL_SIZE))) //指向的内存块未超出内存池范围时,继续执行循环
{
if (!p_current_block->allocated) {
MemoryBlock_t* p_next_block = (MemoryBlock_t*)((uint8_t*)p_current_block + sizeof(MemoryBlock_t) + p_current_block->size);
if ((p_next_block < (MemoryBlock_t*)(g_memory_pool + MEMORY_POOL_SIZE)) && !p_next_block->allocated)
{// 如果下一个内存块也是空闲的,则将其合并到当前内存块
p_current_block->size += (sizeof(MemoryBlock_t) + p_next_block->size);
}
else
{
/* 如果下一个内存块不是空闲的,则把其移到前边
这样做还有一个好处,例如现在的内存分配是10k
创建第一个块4k,第二个块5k
释放第一个块
这时候,如果不移动,则无法继续创建一个3k的块
而移动之后就有足够的连续的空闲空间了
*/
Memorylist_t* p_temp_node = p_rear_node;
do{
if(p_temp_node->p_address == (MemoryBlock_t*)p_next_block+1 )
{
p_temp_node->p_address = (MemoryBlock_t*)p_current_block + 1;
break;
}
p_temp_node = p_temp_node->p_prior;
}while(p_temp_node != NULL);
int temp_size_current_block = p_current_block->size;
int temp_size_next_block = p_next_block->size;
memmove(p_current_block, p_next_block, (temp_size_next_block + sizeof(MemoryBlock_t)));
p_temp_node->p_block = p_current_block;
p_current_block = (MemoryBlock_t*)((char*)p_current_block+ sizeof(MemoryBlock_t) + temp_size_next_block);
p_current_block->allocated = 0;
p_current_block->size = temp_size_current_block;
continue;
}
}
p_current_block = (MemoryBlock_t*)((uint8_t*)p_current_block + sizeof(MemoryBlock_t) + p_current_block->size);
}
free(*ptr);
}
}
void print_all_node()
{
Memorylist_t* p_temp_node = p_rear_node;
do{
printf("size = %05d, p_address = %d\r\n", p_temp_node->p_block->size, p_temp_node->p_address);
p_temp_node = p_temp_node->p_prior;
}while(p_temp_node != NULL);
}
int main() {
initialize_g_memory_pool(); // 初始化内存池
int j = 0;
printf("内存范围:%d - %d\r\n", g_memory_pool, g_memory_pool+MEMORY_POOL_SIZE);
///
Memorylist_t* ptr1 = my_malloc(40); // 分配 40 字节内存
if(ptr1 == NULL)
{
printf("内存分配失败\r\n");
return 0;
}
/*模拟写入数据*/
uint8_t *a1;
a1 = (uint8_t*)(ptr1->p_address);
j = 0;
for(int i = 0;i< ptr1->p_block->size;i++)
{
a1[i] = j++;
}
printf("Allocated memory p_address 1: %d\n", ptr1->p_address);
///
Memorylist_t* ptr2 = my_malloc(50); // 分配 50 字节内存
if(ptr2 == NULL)
{
printf("内存分配失败\r\n");
return 0;
}
/*模拟写入数据*/
uint8_t *a2;
j=12;
a2 = (uint8_t*)(ptr2->p_address);
for(int i = 0;i< ptr2->p_block->size;i++)
{
a2[i] = j++;
}
printf("Allocated memory p_address 2: %d\n", ptr2->p_address);
///
my_free(&ptr1); // 释放 40 字节内存
printf("Allocated memory p_address 2: %d //释放内存块1之后,会把后面的内存块向前移\n", ptr2->p_address);
///
Memorylist_t* ptr3 = my_malloc(20); // 再次分配 20 字节内存
if(ptr3 == NULL)
{
printf("内存分配失败\r\n");
return 0;
}
/*模拟写入数据*/
uint8_t *a3;
j=21;
a3 = (uint8_t*)(ptr3->p_address);
for(int i = 0;i< ptr3->p_block->size;i++)
{
a3[i] = j++;
}
printf("Allocated memory p_address 3: %d\n", ptr3->p_address);
///
my_free(&ptr2); // 释放 50 字节内存
printf("Allocated memory p_address 3: %d //释放内存块1之后,会把后面的内存块向前移\n", ptr3->p_address);
///
Memorylist_t* ptr4 = my_malloc(60); // 充分利用释放的内存,分配 30 字节内存
if(ptr4 == NULL)
{
printf("内存分配失败\r\n");
return 0;
}
/*模拟写入数据*/
uint8_t *a4;
j = 34;
a4 = (uint8_t*)(ptr4->p_address);
for(int i = 0;i< ptr4->p_block->size;i++)
{
a4[i] = j++;
}
printf("Allocated memory p_address 4: %d\n", ptr4->p_address);
///
char* p;
/*取出数据*/
// p = (char*)(ptr2->p_address);
// for(int i = 0;i< ptr2->p_block->size;i++)
// {
// printf("%d ",*p++);
// }
printf("\r\n");
/*取出数据*/
p = (char*)(ptr3->p_address);
for(int i = 0;i< ptr3->p_block->size;i++)
{
printf("%d ",*p++);
}
printf("\r\n");
/*取出数据*/
p = (char*)(ptr4->p_address);
for(int i = 0;i< ptr4->p_block->size;i++)
{
printf("%d ",*p++);
}
printf("\r\n");
print_all_node();
return 0;
}