RT-Thread--内存管理

内存管理的功能特点

  • RT-Thread 操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法。总体上可分为两类:内存堆管理与内存池管理,而内存堆管理又根据具体内存设备划分为三种情况:

    1. 第一种是针对小内存块的分配管理(小内存管理算法);

    2. 第二种是针对大内存块的分配管理(slab 管理算法);

    3. 第三种是针对多内存堆的分配情况(memheap 管理算法)

内存堆管理

  • 内存堆管理用于管理一段连续的内存空间,如下图所示,RT-Thread 将 “ZI 段结尾处” 到内存尾部的空间用作内存堆。

  • 小内存管理算法主要针对系统资源比较少,一般用于小于 2MB 内存空间的系统;
  • slab 内存管理算法则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法;
  • RT-Thread 还有一种针对多内存堆的管理算法,即 memheap 管理算法。memheap 方法适用于系统存在多个内存堆的情况,它可以将多个内存 “粘贴” 在一起,形成一个大的内存堆;
  • 注意:因为内存堆管理器要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以不要在中断服务例程中分配或释放动态内存块,因为它可能会引起当前上下文被挂起等待;

小内存管理里算法

  •  当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来,如图所示:

  • 每个内存块都包含一个数据头:magic 和 used
  • magic:用来标记这个内存块是一个内存管理的内存数据块,也是一个内存保护字,如果这个区域被改写,那么这个内存块就被非法改写了;
  • used :用来表示当前内存块是否被分配;
  • 内存分配过程:
  • 设定:空闲链表指针Ifree初始指向32字节的内存块,当用户线程需要分配一个64字节的内存块时,Ifree指针指向的内存块不能满足要求,内存管理器就会继续寻找下一个内存块,当找到时(128字节),就会进行内存分配,如果内存块比较大,分配器就会把内存块进行拆分,余下的内存(52字节)继续留在Ifree链表中,如下图所示;

  • 注意:在内次分配内存块前,都会留出12字节用于magic、used信息及节点使用,返回给应用的地址实际上是这块内存块 12 字节以后的地址,前面的 12 字节数据头是用户永远不应该碰的部分(注:12 字节数据头长度会与系统对齐差异而有所不同)。
  • 释放:释放内存时,分配器会查看前后相邻的内存是否是空闲,如果空闲,就回合并成一个大的空闲内存块; 

slab管理算法

  • RT-Thread 的 slab 分配器的实现是建立在 slab分配器上的,针对嵌入式仙童优化的内存分配算法。(slab是linux系统中的一种内存分配机制)
  • RT-Thread 的 slab 分配器实现主要是去掉了其中的对象构造及析构过程,只保留了纯粹的缓冲型的内存池算法。slab 分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池,如下图所示:

  • 一个 zone 的大小在 32K 到 128K 字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中的 zone 最多包括 72 种对象,一次最大能够分配 16K 的内存空间,如果超出了 16K 那么直接从页分配器中分配。每个 zone 上分配的内存块大小是固定的,能够分配相同大小内存块的 zone 会链接在一个链表中,而 72 种对象的 zone 链表则放在一个数组(zone_array[])中统一管理。
  •  内存分配:
  • 假设分配一个 32 字节的内存,slab 内存分配器会先按照 32 字节的值,从 zone array 链表表头数组中找到相应的 zone 链表。如果这个链表是空的,则向页分配器分配一个新的 zone,然后从 zone 中返回第一个空闲内存块。如果链表非空,则这个 zone 链表中的第一个 zone 节点必然有空闲块存在(否则它就不应该放在这个链表中),那么就取相应的空闲块。如果分配完成后,zone 中所有空闲内存块都使用完毕,那么分配器需要把这个 zone 节点从链表中删除。
  • 内存释放:分配器需要找到内存块所在的 zone 节点,然后把内存块链接到 zone 的空闲内存块链表中。如果此时 zone 的空闲链表指示出 zone 的所有内存块都已经释放,即 zone 是完全空闲的,那么当 zone 链表中全空闲 zone 达到一定数目后,系统就会把这个全空闲的 zone 释放到页面分配器中去。

memheap管理算法

  •  memheap 管理算法适用于系统含有多个地址可不连续的内存堆。使用 memheap 内存管理可以简化系统存在多个内存堆时的使用:当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的 memheap 初始化,并开启 memheap 功能就可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配;
  • 在开启 memheap 之后原来的 heap 功能将被关闭,两者只可以通过打开或关闭 RT_USING_MEMHEAP_AS_HEAP 来选择其一;
  • memheap 工作机制如下图所示,首先将多块内存加入 memheap_item 链表进行粘合。当分配内存块时,会先从默认内存堆去分配内存,当分配不到时会查找 memheap_item 链表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是在操作一个内存堆。

内存堆配置和初始化

  •  在使用内存堆时,必须要在系统初始化的时候进行堆的初始化;
  • /**
     * @ingroup SystemInit
     *
     * This function will initialize system heap memory.
     *
     * @param begin_addr the beginning address of system heap memory.
     * @param end_addr the end address of system heap memory.
     */
    void rt_system_heap_init(void *begin_addr, void *end_addr)
  • 在使用 memheap 堆内存时,必须要在系统初始化的时候进行堆内存的初始化;
  • /*
     * The initialized memory pool will be:
     * +-----------------------------------+--------------------------+
     * | whole freed memory block          | Used Memory Block Tailer |
     * +-----------------------------------+--------------------------+
     *
     * block_list --> whole freed memory block
     *
     * The length of Used Memory Block Tailer is 0,
     * which is prevents block merging across list
     */
    rt_err_t rt_memheap_init(struct rt_memheap *memheap,
                             const char        *name,
                             void              *start_addr,
                             rt_uint32_t        size)

内存堆的管理方式

  • 对内存堆的操作包含:初始化、申请内存块、释放内存,所有使用完成后的动态内存都应该被释放,以供其他程序的申请使用;

内存分配和释放

  • 从内存堆上分配用户指定大小的内存块,t_malloc 函数会从系统堆空间中找到合适大小的内存块,然后把内存块可用地址返回给用户;
  • /**
     * Allocate a block of memory with a minimum of 'size' bytes.
     *
     * @param size is the minimum size of the requested block in bytes.
     *
     * @return pointer to allocated memory or NULL if no free memory was found.
     */
    void *rt_malloc(rt_size_t size)
  •  应用程序使用完从内存分配器中申请的内存后,必须及时释放,否则会造成内存泄漏

  • /**
     * This function will release the previously allocated memory block by
     * rt_malloc. The released memory block is taken back to system heap.
     *
     * @param rmem the address of memory which will be released
     */
    void rt_free(void *rmem) 

重分配内存块

  •  在已分配内存块的基础上重新分配内存块的大小(增加或缩小)
    /**
     * This function will change the previously allocated memory block.
     *
     * @param rmem pointer to memory allocated by rt_malloc
     * @param newsize the required new size
     *
     * @return the changed memory block address
     */
    void *rt_realloc(void *rmem, rt_size_t newsize)

分配多内存块

  •  从内存堆中分配连续内存地址的多个内存块;
  • /**
     * This function will contiguously allocate enough space for count objects
     * that are size bytes of memory each and returns a pointer to the allocated
     * memory.
     *
     * The allocated memory is filled with bytes of value zero.
     *
     * @param count number of objects to allocate
     * @param size size of the objects to allocate
     *
     * @return pointer to allocated memory / NULL pointer if there is an error
     */
    void *rt_calloc(rt_size_t count, rt_size_t size)

设置内存钩子函数

  •  在分配内存块过程中,用户可设置一个钩子函数。设置的钩子函数会在内存分配完成后进行回调。回调时,会把分配到的内存块地址和大小做为入口参数传递进去;
  • /**
     * This function will set a hook function, which will be invoked when a memory
     * block is allocated from heap memory.
     *
     * @param hook the hook function
     */
    void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size))
    {
        rt_malloc_hook = hook;
    }
  • 在释放内存时,用户可设置一个钩子函数;

  • /**
     * This function will set a hook function, which will be invoked when a memory
     * block is released to heap memory.
     *
     * @param hook the hook function
     */
    void rt_free_sethook(void (*hook)(void *ptr))
    {
        rt_free_hook = hook;
    }

内存堆管理应用示例

  • 创建一个动态的线程,这个线程会动态申请内存并释放,每次申请更大的内存,当申请不到的时候就结束;
  • #include <rtthread.h>
    
    #define THREAD_PRIORITY      25
    #define THREAD_STACK_SIZE    512
    #define THREAD_TIMESLICE     5
    
    /* 线程入口 */
    void thread1_entry(void *parameter)
    {
        int i;
        char *ptr = RT_NULL; /* 内存块的指针 */
    
        for (i = 0; ; i++)
        {
            /* 每次分配 (1 << i) 大小字节数的内存空间 */
            ptr = rt_malloc(1 << i);
    
            /* 如果分配成功 */
            if (ptr != RT_NULL)
            {
                rt_kprintf("get memory :%d byte\n", (1 << i));
                /* 释放内存块 */
                rt_free(ptr);
                rt_kprintf("free memory :%d byte\n", (1 << i));
                ptr = RT_NULL;
            }
            else
            {
                rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
                return;
            }
        }
    }
    
    int dynmem_sample(void)
    {
        rt_thread_t tid = RT_NULL;
    
        /* 创建线程 1 */
        tid = rt_thread_create("thread1",
                               thread1_entry, RT_NULL,
                               THREAD_STACK_SIZE,
                               THREAD_PRIORITY,
                               THREAD_TIMESLICE);
        if (tid != RT_NULL)
            rt_thread_startup(tid);
    
        return 0;
    }
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(dynmem_sample, dynmem sample);

    运行结果:

  • \ | /
    - RT -     Thread Operating System
     / | \     3.1.0 build Aug 24 2018
     2006 - 2018 Copyright by rt-thread team
    msh >dynmem_sample
    msh >get memory :1 byte
    free memory :1 byte
    get memory :2 byte
    free memory :2 byteget memory :16384 byte
    free memory :16384 byte
    get memory :32768 byte
    free memory :32768 byte
    try to get 65536 byte memory failed!

内存池

内存池工作机制

内存池的管理方式

内存池应用示例

 参考

  • 《RT-Thread 编程指南》

猜你喜欢

转载自www.cnblogs.com/icefree/p/10822971.html