C语言实现模拟内存池管理,可用于stm32等内存较少的环境中

本文介绍了在STM32F407单片机上如何使用malloc和free进行动态内存管理,以及遇到内存资源受限时,如何通过自定义内存池来提高内存利用效率,避免内存碎片问题。
摘要由CSDN通过智能技术生成

以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;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值