当前位置:   article > 正文

自己实现堆的动态分配——my_malloc和my_free_c语言中用动态分配函数malloc和free来管理“堆”,堆分配代码实现

c语言中用动态分配函数malloc和free来管理“堆”,堆分配代码实现


前言

  • 为什么需要使用动态内存分配?

在嵌入式应用开发中,常常会遇到RAM空间大小有限的情况,这时需要在使用有限空间的过程中精打细算,比如整块RAM的空间只有100bytes,现在需要划分一段空间去存储来自串口接收的数据,然而接收的这段数据长度是不固定的,范围在[10bytes,70bytes]之间,如果不使用动态内存分配,为了保证接收到所有数据,则会直接划分固定70bytes的空间去存储数据,这样当只收到10bytes的数据时,将会造成60bytes的空间浪费。在使用动态内存分配后,可以使得需要多少空间就划分多少空间。

  • 为什么不使用库里自带的malloc,free函数

C库中实现的malloc,free过于复杂,执行时间相对而言太长,不适合应用在嵌入式系统中。另外,库里的malloc不可重入,在多线程的情况下并不适用,在freertos的heap3.c中,使用库里的malloc函数前后都会挂起任务调度器。

  • 本文中实现的动态内存分配方案具备的特点
    1 实现方案简洁
    2 可以管理多块逻辑上不连续的内存空间
    3 解决多次free后内存碎片化的问题

一、代码浅析

  • 定义
#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分别为对应空间的起始及终止地址
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 管理方法
/* 将自己定义的大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();
}
  • 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

二、示例演示

提示:在以下动图中:

  • 黄色区域表示链表项,类型为mem_heap_t,用于串联前后两个memory block。
  • 绿色区域表示用于存放数据的空间,即用户使用malloc返回的空间
  • 白色区域表示free的空间,即未被使用的区域

接下来通过一部分代码及对应动图直观的体会下。

  • register空间
    mem_register(MEM_GENERAL, heap_buf, CONFIG_TOTAL_HEAP_SIZE);  // 注册自己定义的大数组作为heap,并初始化首位节点
  • 1

在这里插入图片描述

  • malloc空间
	uint32_t *p1 = mem_malloc(MEM_GENERAL, 5);
    uint32_t *p2 = mem_malloc(MEM_GENERAL, 4);
    uint32_t *p3 = mem_malloc(MEM_GENERAL, 5);
  • 1
  • 2
  • 3

在这里插入图片描述

  • free空间
    mem_free(MEM_GENERAL, p2);
    mem_free(MEM_GENERAL, p1);
    mem_free(MEM_GENERAL, p3);
  • 1
  • 2
  • 3

在这里插入图片描述


总结

本文介绍的内存分配的方法的一个亮点在于,当释放地址时,只需要将该地址的节点去掉就行,相比freertos的实现方法简单了不少。结合前方的代码以及动图演示,各位应该都能比较容易理解,over!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/969355
推荐阅读
相关标签
  

闽ICP备14008679号