• 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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303

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

  • 相关阅读:
    MySQL的二进制安装
    基于eNSP中大型校园/企业网络规划与设计_综合大作业
    【Python】函数(function)和方法(method)的区别
    Spring 深入——IoC 容器 02
    MySQL之BETWEEN AND包含范围查询总结
    论文被多人研究过了,我还可以怎么写?
    论文阅读:Duplex Contextual Relation Network for Polyp Segmentation
    【组件】Vue组件之间的通信父传子 | 子传父
    C++中的强制转换
    Kmssink插件添加缩放显示功能的分析思路与具体实现
  • 原文地址:https://blog.csdn.net/qq_36561846/article/details/134006485