29、分块式内存管理[malloc、free]

内存管理的实现方法有很多种,其实最终都是要实现两个函数:malloc 和 free。malloc 函数用来内存申请,free 函数用于内存释放。本文通过分块式内存管理的方式实现:
在这里插入图片描述
只不过代码中分配方向为1->n
没有使用正点原子的代码,而是自己封装实现了,方便添加多个内存进行管理
/**********************************************************************
*file:内存管理文件
*author:残梦
*versions:V1.0
*date:2023.10.23
*note:注:本内存管理采用分块式方式
内存块0 内存块1 内存块2 … 内存块n
内存表0 内存表1 内存表2 … 内存表3
分配方向:0->n
内存表采用4字节uint32_t定义,单次分配字节数不可大于此0xFFFFFFFF
内存管理自身消耗内存=单个内存表4字节 * 内存块数 + sizeof(memory_manage_StructDef) + 地址对齐损失字节数
更多可以参考正点原子的内存分配,本文件思路采用正点原子,但是更推荐使用FreeRTOS的heap4思路

使用方式:
1、添加内存枚举及名称:MemoryList_EnumDef 和 Name_MemoryList
2、分配内存大小及内存数组:dMemory0_Size 和 memory0_heap[dMemory0_Size]
3、调用memory_init()初始化内存管理(可以修改单个内存块字节大小,dAlign_Byte的整数倍)
4、可以开始使用memory_malloc()、memory_realloc()、memory_free()
5、内存剩余百分比:memory_SurplusRatio()
注:使用内存分配memory_malloc()\memory_realloc()后,不使用此资源后需使用memory_free()释放内存,避免内存泄露|不够
**********************************************************************/

memory_driver.h

#ifndef _memory_driver_H_
#define _memory_driver_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"

//内存0--参数
#define dMemory0_Size ((uint32_t )(5*1024)) //分配内存大小

typedef enum
{
    eMemory_SRAM_Inside = 0,//内部SRAM
    eMemory_Number//内存数
}MemoryList_EnumDef;//内存类别

int32_t memory_init(void);
void *memory_malloc(MemoryList_EnumDef memory,uint32_t size);
void memory_free(MemoryList_EnumDef memory,void *ptr);
void *memory_realloc(MemoryList_EnumDef memory,void *ptr,uint32_t size);
float memory_SurplusRatio(MemoryList_EnumDef memory);
void memory_test(void);

#ifdef __cplusplus
}
#endif
#endif

memory_driver.c

/**********************************************************************
*file:内存管理文件
*author:残梦
*versions:V1.0
*date:2023.10.23
*note:注:本内存管理采用分块式方式
内存块0 内存块1 内存块2 ...... 内存块n
内存表0 内存表1 内存表2 ...... 内存表3
分配方向:0->n
内存表采用4字节uint32_t定义,单次分配字节数不可大于此0xFFFFFFFF
内存管理自身消耗内存=单个内存表4字节 * 内存块数 + sizeof(memory_manage_StructDef) + 地址对齐损失字节数
更多可以参考正点原子的内存分配,本文件思路采用正点原子,但是更推荐使用FreeRTOS的heap4思路

使用方式:
1、添加内存枚举及名称:MemoryList_EnumDef 和 Name_MemoryList
2、分配内存大小及内存数组:dMemory0_Size 和 memory0_heap[dMemory0_Size]
3、调用memory_init()初始化内存管理(可以修改单个内存块字节大小,dAlign_Byte的整数倍)
4、可以开始使用memory_malloc()、memory_realloc()、memory_free()
5、内存剩余百分比:memory_SurplusRatio()
注:使用内存分配memory_malloc()\memory_realloc()后,不使用此资源后需使用memory_free()释放内存,避免内存泄露|不够
**********************************************************************/
#include "memory_driver.h"
#include "stdio.h"

#define dDebug_Printf //开启调试打印信息

//格式参数
#define dAlign_Byte (4) //对齐字节数
#define dAlign_Mask (dAlign_Byte - 1) //对齐掩码

typedef struct
{
    uint32_t block;//内存块总数
    uint32_t address;//内存池起始地址
    uint32_t size_block;//内存块大小,dAlign_Byte的整数倍;单位-字节
    uint32_t *table;//内存池管理表:0--未使用,!0--连续占用内存块

    //统计使用资源
    uint32_t block_surplus;//剩余内存块
}memory_manage_StructDef;//内存堆具体分配参数

static const char Name_MemoryList[eMemory_Number][12] = {"SRAM_Inside"};
static memory_manage_StructDef *memory_manage[eMemory_Number];

//需要自己定义的内存池数组
static uint8_t memory_status[eMemory_Number] = {0};//内存可用状态,0--未初始化;1--已初始化
static uint8_t memory0_heap[dMemory0_Size] = {0};//内存0数组;使用其他内存时,可以指定数组地址位置,如:uint32_t pbuffer[(32*1024*1024)/4] __attribute__((at(0xC0000000)));//0xC0000000是SDRAM1的起始地址

static int32_t memory_init_assign(MemoryList_EnumDef type,uint32_t StartAddress,uint32_t size_total,uint32_t size_block);

/****************************************
@function:内存管理初始化指定SRAM
@param: type--内存类型
        StartAddress--内存起始地址
        size_total--内存大小,不得低于1KB;单位-字节
        size_block--内存块大小,dAlign_Byte的整数倍,且不低于dAlign_Byte * 8;单位-字节
@return:0--初始化成功,!0初始化失败
@note:
    -1--参数错误
****************************************/
static int32_t memory_init_assign(MemoryList_EnumDef type,uint32_t StartAddress,uint32_t size_total,uint32_t size_block)
{
    uint32_t address = 0;
    uint32_t xTotalHeapSize = 0;
    uint32_t block = 0,block_self = 0,x = 0;
    int64_t i = 0;

    if((type >= eMemory_Number)\
        || (size_total < 1024)\
        || (size_block < (dAlign_Byte * 8))\
        || (size_block % dAlign_Byte))return -1;

    //对地址进行对齐;地址对齐后:起始地址address,可用内存大小:xTotalHeapSize
    address = StartAddress;
    xTotalHeapSize = size_total;
    if((address & dAlign_Mask) != 0)
    {
        address += dAlign_Mask;
        address &= ~((uint32_t ) dAlign_Mask);
        xTotalHeapSize -= (address - StartAddress);
    }
    
    //划分内存块 内存块数=block,起始地址=address
    block = xTotalHeapSize / size_block;

    //分配所需消耗资源,内存表
    x = sizeof(memory_manage_StructDef ) + block*4;//内存表内存块大小=4字节
    block_self = x / size_block;
    if(x % size_block)block_self++;
    memory_manage[type] = (memory_manage_StructDef *)address;
    memory_manage[type]->block = block;
    memory_manage[type]->address = address;
    memory_manage[type]->size_block = size_block;
    memory_manage[type]->table = (uint32_t *)(address + sizeof(memory_manage_StructDef ));
    memory_manage[type]->block_surplus = block;

    //内存管理表设置:分配方向0->n
    for(i=0;i < memory_manage[type]->block;i++)
    {
        if(i < block_self)//内存管理资源占用
        {
            memory_manage[type]->table[i] = block_self;
            memory_manage[type]->block_surplus--;
        }
        else {memory_manage[type]->table[i] = 0;}//内存管理表清零
    }
    memory_status[type] = 1;

#ifdef dDebug_Printf
    printf("内存类别:%s\t内存总大小:%.3f%s\n内存起始地址:0x%0x\t内存块大小:%dbyte\n内存块起始地址:0x%0x\t内存块总数:%d\n可用内存块数:%d\n",\
        Name_MemoryList[type],\
        ((size_total < 1024)?((float)size_total):((size_total < 1024*1024)?((float)size_total/1024.0f):((float)size_total/1024.0f/1024.0f))),\
        ((size_total < 1024)?"Byte":((size_total < 1024*1024)?"KB":"MB")),\
        StartAddress,memory_manage[type]->size_block,\
        memory_manage[type]->address,memory_manage[type]->block,memory_manage[type]->block_surplus);
#endif

    return 0;
}

/****************************************
@function:内存管理初始化
@param:void
@return:-1--失败,0--成功
@note:
****************************************/
int32_t memory_init(void)
{
    if(memory_init_assign(eMemory_SRAM_Inside,(uint32_t )memory0_heap,dMemory0_Size,64) < 0){printf("Error memory_init:%s initialization failed...\n",Name_MemoryList[eMemory_SRAM_Inside]);return -1;}
    return 0;
}

/****************************************
@function:分配所需的内存空间,并返回一个指向它的指针
@param: memory--待分配内存源
        size--分配内存大小,单位字节
@return:函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
@note:
****************************************/
void *memory_malloc(MemoryList_EnumDef memory,uint32_t size)
{
    uint8_t flag = 0;
    uint32_t block = 0,cnt = 0,address = 0,offset = 0;
    int64_t i = 0;

    if((memory >= eMemory_Number)\
        || (memory_manage[memory]->block_surplus == 0)\
        || (size == 0)\
        || (memory_status[memory] == 0))
    {return NULL;}

    //计算所需内存块数
    block = size / memory_manage[memory]->size_block;
    if(size % memory_manage[memory]->size_block)block++;
    if(block > memory_manage[memory]->block)return NULL;

    //寻找可用的内存块
    for(i= 0,flag = 0,cnt = 0;i < memory_manage[memory]->block;i++)
    {
        if(memory_manage[memory]->table[i]){cnt = 0;}
        else
        {
            cnt++;
            if(cnt >= block){flag = 1;break;}
        }
    }
    if(!flag)return NULL;//内存不足
    offset = i - block + 1;//已经寻找到足够的内存块,内存块起始点=offset,块数block
    
    //标记内存块
    for(i=offset;i < (offset + block);i++)
    {
        memory_manage[memory]->table[i] = block;
        memory_manage[memory]->block_surplus--;
    }

    address = memory_manage[memory]->address + offset * memory_manage[memory]->size_block;//分配内存的起始地址
    return (void *)address;
}

/****************************************
@function:释放之前调用 memory_malloc 或 memory_realloc 所分配的内存空间
@param: memory--待分配内存源
        ptr--指针指向一个要释放内存的内存块,该内存块之前是通过调用memory_malloc 或 memory_realloc进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
@return:void
@note:
****************************************/
void memory_free(MemoryList_EnumDef memory,void *ptr)
{
    uint32_t address = 0,offset = 0,i = 0,cnt = 0;

    if((memory >= eMemory_Number)\
        || (ptr == NULL)\
        || (memory_status[memory] == 0))
    {return;}

    address = (uint32_t )ptr;//获取地址
    offset = (address - memory_manage[memory]->address) / memory_manage[memory]->size_block;
    cnt = memory_manage[memory]->table[offset];
    for(i = 0;i < cnt;i++)
    {
        memory_manage[memory]->table[offset + i] = 0;
        memory_manage[memory]->block_surplus++;
    }
    ptr = NULL;
}

/****************************************
@function:重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小
@param: memory--待分配内存源
        ptr--指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 memory_malloc、memory_realloc进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
        size--内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
@return:函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
@note:
****************************************/
void *memory_realloc(MemoryList_EnumDef memory,void *ptr,uint32_t size)
{
    void *p = NULL;
    uint32_t *address = NULL,*address_last = NULL;
    uint32_t size_last = 0,i = 0;

    if((memory >= eMemory_Number)\
        || (size == 0)\
        || (memory_status[memory] == 0))
    {return NULL;}

    if(ptr == NULL)return memory_malloc(memory,size);//ptr未分配过内存
    size_last = memory_manage[memory]->table[((uint32_t )ptr - memory_manage[memory]->address)/memory_manage[memory]->size_block] * memory_manage[memory]->size_block;
    if(size_last > size)return NULL;//之前分配的内存比当前需求大

    //分配新的内存
    p = memory_malloc(memory,size);
    if(p == NULL)return NULL;

    //拷贝之前的数据至新的位置
    address = (uint32_t *)p;
    address_last = (uint32_t *)ptr;
    for(i = 0;i < size_last/4;i++){address[i] = address_last[i];}
    memory_free(memory,ptr);
    return p;
}

/****************************************
@function:内存可用空间百分比
@param: memory--待分配内存源

@return:void
@note:注:未包括地址对齐损失字节
****************************************/
float memory_SurplusRatio(MemoryList_EnumDef memory)
{
    if((memory >= eMemory_Number) || (memory_status[memory] == 0))return 0.0f;
    return ((float )memory_manage[memory]->block_surplus / (float )memory_manage[memory]->block * 100.0f);
}

/****************************************
@function:内存管理测试函数
@param:void
@return:void
@note:打印的err = 0表示正确
    nblock:%d / %d为剩余内存块/总内存块数
****************************************/
void memory_test(void)
{
    uint32_t *size = NULL;
    uint32_t err = 0,i = 0;
    printf("nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);
    size = (uint32_t *)memory_malloc(eMemory_SRAM_Inside,64*32);
    printf("nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);

    for(i = 0;i < 64*32/4;i++) *(size + i) = i;
    size = memory_realloc(eMemory_SRAM_Inside,(void *)size,64*40);
    printf("nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);

    for(i = 0;i < 64*32/4;i++) if(*(size + i) != i) err++;
    printf("err=%d\n",err);
    
    memory_free(eMemory_SRAM_Inside,size);
    printf("nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);

    float *pfloat = NULL;
    pfloat = (float *)memory_malloc(eMemory_SRAM_Inside,4*64);
    for(i = 0;i < 64;i++) *(pfloat + i) = i*10.0f;
    printf("float nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);
    pfloat = memory_realloc(eMemory_SRAM_Inside,(void *)pfloat,4*64*2);
    for(i = 0,err = 0;i < 64;i++) if((uint32_t)pfloat[i] != i*10)err++;
    printf("float err=%d\n",err);
    memory_free(eMemory_SRAM_Inside,pfloat);
    printf("float nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);

    uint8_t *pu8 = NULL;
    pu8 = (uint8_t *)memory_malloc(eMemory_SRAM_Inside,4*64);
    for(i = 0;i < 4*64;i++) *(pu8 + i) = i;
    printf("uint8_t nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);
    pu8 = (uint8_t *)memory_realloc(eMemory_SRAM_Inside,(void *)pu8,4*64*2);
    for(i = 0,err = 0;i < 4*64;i++) if((uint32_t)pu8[i] != i)err++;
    printf("uint8_t err=%d\n",err);
    memory_free(eMemory_SRAM_Inside,pu8);
    printf("uint8_t nblock:%d / %d\n",memory_manage[eMemory_SRAM_Inside]->block_surplus,memory_manage[eMemory_SRAM_Inside]->block);

    while(1);
}

源码文件下载:
链接:https://pan.baidu.com/s/1iT5s587Rl1wtSdtHCPPwrw
提取码:diep

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值