rt-thread-------内存管理(内存堆)

系列文章目录

rt-thread 之 fal移植
rt-thread 之 生成工程模板
STM32------串口理论篇
rt-thread------串口V1版本(一)配置
rt-thread------串口V1版本(二)发送篇
rt-thread------串口V1版本(三)接收篇


rt-thread内存管理

  • 系列文章目录
  • 前言
  • 1、内存堆的初始化
  • 2.内存申请API
    • rt_malloc
    • rt_realloc
      • rt_calloc
  • 3.内存释放API
    • rt_free
  • 4.内存堆的三种策略
    • 4.1小内存管理法
    • 4.2 slab 管理算法
    • 4.3 memheap 管理算法


前言

简述堆和栈
堆(stack):由编译器自动分配释放
栈(heap):一般由程序员分配和释放


1、内存堆的初始化

rt-thread的内存分配是从ZI段结尾处到内存尾部的空间作为内存堆,如下图所示:
rt-thread-------内存管理(内存堆)_第1张图片
rt-thread中是通过下面函数实现的内存的初始化

void rt_system_heap_init(void *begin_addr, void *end_addr);

实际会在rt_hw_board_init()函数中被调用

rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);

看看这个HEAP_BEGIN 和HEAP_END的定义
先看HEAP_END的定义

#define HEAP_END        STM32_SRAM_END

套了另外一个宏STM32_SRAM_END
实际上本质是下面两个宏定义,我的是STM32F103ZET6,根据芯片手册是64K的SRAM,所以结束地址就是芯片内存最后的地址这一点不难理解。

/* Internal SRAM memory size[Kbytes] <8-64>, Default: 64*/
#define STM32_SRAM_SIZE      64
#define STM32_SRAM_END       (0x20000000 + STM32_SRAM_SIZE * 1024)

再看看HEAP_BEGIN的定义

extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN      ((void *)&Image$$RW_IRAM1$$ZI$$Limit)

Image$$RW_IRAM1$$ZI$$Limit这是一个外部变量,但是我通过工程无法找到这个变量,但是我在kei工程的生成的map文件中找到了这个变量
rt-thread-------内存管理(内存堆)_第2张图片
所以我猜这是链接器分配ZI字段结束的地址。
以下只针对ARM_CC编译器
rt-thread-------内存管理(内存堆)_第3张图片
rt_system_heap_init函数展开的细节这里不深究了,如果非要刨根问底的话可以参考这篇文章

当使用memheap内存分配策略时还需要调用,对不连续内存块分别初始化,并且加入 memheap_item 链表。

rt_err_t rt_memheap_init(struct rt_memheap  *memheap,
                        const char  *name,
                        void        *start_addr,
                        rt_uint32_t size)

2.内存申请API

rt_malloc

API

void *rt_malloc(rt_size_t nbytes);

使用方法和逻辑的malloc一样。rt_malloc函数会从系统堆空间中找到合适大小的内存块,若无法申请则返回RT_NULL所以程序中对此函数的非空判断还是不能省略的。

rt_realloc

API

void *rt_realloc(void *rmem, rt_size_t newsize);

在进行重新分配内存块时,原来的内存块数据保持不变(缩小的情况下,后面的数据被自动截断)。若重新分配的内存块变大,则会在rmem内存后面增加未被初始化的内存块。

rt_calloc

API

  void *rt_calloc(rt_size_t count, rt_size_t size);

从内存堆中分配连续内存地址的多个内存块。

3.内存释放API

rt_free

API

void rt_free (void *ptr);

rt_free 函数会把待释放的内存还回给堆管理器中。在调用这个函数时用户需传递待释放的内存块指针,如果是空指针直接返回

4.内存堆的三种策略

无论选择哪个策略,都会可以使用上面的API接口。对内存进行操作。

4.1小内存管理法

初始时,它是一块大的内存。当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来。
rt-thread-------内存管理(内存堆)_第4张图片
rt_free()时会把used设置成未使用,下次申请内存时会先遍历到没使用的内存块,然后判断申请的内存是否小于分配好的未使用的内存块。若小于直接使用,并且在数据后面将剩余内存生成一个链表头,标记为未使用。若大于则继续遍历,若没有找到合适大小的内存堆,则在最后未分配的内存堆中分配该大小的内存。
这种小内存管理方法若频繁rt_malloc()申请不同大小的内存堆然后再rt_free()必然产生大量内存碎片。再看看rt_free()函数。

/**
 * @brief This function will release the previously allocated memory block by
 *        rt_mem_alloc. The released memory block is taken back to system heap.
 *
 * @param rmem the address of memory which will be released.
 */
void rt_smem_free(void *rmem)
{
    struct rt_small_mem_item *mem;
    struct rt_small_mem *small_mem;

    if (rmem == RT_NULL)
        return;

    RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0);

    /* Get the corresponding struct rt_small_mem_item ... */
    mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);

    RT_DEBUG_LOG(RT_DEBUG_MEM,
                 ("release memory 0x%x, size: %d\n",
                  (rt_ubase_t)rmem,
                  (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr))));

    /* ... which has to be in a used state ... */
    small_mem = MEM_POOL(mem);
    RT_ASSERT(small_mem != RT_NULL);
    RT_ASSERT(MEM_ISUSED(mem));
    RT_ASSERT(rt_object_get_type(&small_mem->parent.parent) == RT_Object_Class_Memory);
    RT_ASSERT(rt_object_is_systemobject(&small_mem->parent.parent));
    RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr &&
              (rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end);
    RT_ASSERT(MEM_POOL(&small_mem->heap_ptr[mem->next]) == small_mem);

    /* ... and is now unused. */
    mem->pool_ptr = MEM_FREED();
#ifdef RT_USING_MEMTRACE
    rt_smem_setname(mem, "    ");
#endif /* RT_USING_MEMTRACE */

    if (mem < small_mem->lfree)
    {
        /* the newly freed struct is now the lowest */
        small_mem->lfree = mem;
    }

    small_mem->parent.used -= (mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr));

    /* finally, see if prev or next are free also */
    plug_holes(small_mem, mem);
}

rt_free()的最后有个plug_hoes()其作用就是检查释放内存的前面一个内存块和后面一个内存块。若未使用则将其合并,生成一个新的内存块。有了内存块合并可以大大减少内存碎片的产生。

4.2 slab 管理算法

RT-Thread 的 slab 分配器是在 DragonFly BSD 创始人 Matthew Dillon 实现的 slab 分配器基础上,针对嵌入式系统优化的内存分配算法。最原始的 slab 算法是 Jeff Bonwick 为 Solaris 操作系统而引入的一种高效内核内存分配算法。

RT-Thread 的 slab 分配器实现主要是去掉了其中的对象构造及析构过程,只保留了纯粹的缓冲型的内存池算法。slab 分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池
rt-thread-------内存管理(内存堆)_第5张图片
slab算法堆单片机sram要求比较高,一般>2M的才会使用,详细的算法以及说明可以看官方的手册

4.3 memheap 管理算法

当使用的芯片有两个块SRAM(如STM32F427)或者外挂SRAM时可以使用此种内存管理方法。
rt-thread-------内存管理(内存堆)_第6张图片

rt_err_t rt_memheap_init(struct rt_memheap  *memheap,
                        const char  *name,
                        void        *start_addr,
                        rt_uint32_t size)

如果有多个不连续的 memheap 可以多次调用该函数将其初始化并加入 memheap_item 链表。

你可能感兴趣的:(#,RTOS,内存管理,rt-thread,内存堆)