静态内存池管理
静态内存池工作原理
内存池管理结构示意图 是内存池管理结构示意图。内存池(Memory Pool)是一种用于分配大量大小相同的小对象的技术。它可以极大加快内存分配/释放的速度。
内存池在创建时先向系统申请一大块内存,然后分成同样大小的多个小内存块,小内存块直接通过链表连接起来(此链表也称为空闲链表)。每次分配的时候,从空闲链表中取出链头上第一个内存块,提供给申请者。从图中可以看到,物理内存中允许存在多个大小不同的内存池,每一个内存池又由多个空闲内存块组成,内核用它们来进行内存管理。当一个内存池对象被创建时,内存池对象就被分配给了一个内存池控制块,内存控制块的参数包括内存池名,内存缓冲区,内存块大小,块数以及一个等待线程队列。
内核负责给内存池分配内存池对象控制块,它同时也接收用户线程的分配内存块申请,当获得这些信息后,内核就可以从内存池中为内存池分配内存。内存池一旦初始化完成,内部的内存块大小将不能再做调整。
静态内存池控制块
- struct rt_mempool
- {
- struct rt_object parent;
- void *start_address;/* 内存池数据区域开始地址 */
- rt_size_t size; /* 内存池数据区域大小 */
- rt_size_t block_size; /* 内存块大小 */
- rt_uint8_t *block_list; /* 内存块列表 */
- /* 内存池数据区域中能够容纳的最大内存块数 */
- rt_size_t block_total_count;
- /* 内存池中空闲的内存块数 */
- rt_size_t block_free_count;
- /* 因为内存块不可用而挂起的线程列表 */
- rt_list_t suspend_thread;
- /* 因为内存块不可用而挂起的线程数 */
- rt_size_t suspend_thread_count;
- };
- typedef struct rt_mempool* rt_mp_t;
每一个内存池对象由上述结构组成,其中suspend_thread形成了一个申请线程等待列表,即当内存池中无可用内存块,并且申请线程允许等待时,申请线程将挂起在suspend_thread链表上。
静态内存池接口
创建内存池
创建内存池操作将会创建一个内存池对象并从堆上分配一个内存池。创建内存池是从对应内存池中分配和释放内存块的先决条件,创建内存池后,线程便可以从内存池中执行申请、释放等操作。创建内存池使用下面的函数接口,该函数返回一个已创建的内存池对象。
- rt_mp_t rt_mp_create(const char* name, rt_size_t block_count, rt_size_t block_size);
使用该函数接口可以创建一个与需求的内存块大小、数目相匹配的内存池,前提当然是在系统资源允许的情况下(最主要的是动态堆内存资源)才能创建成功。创建内存池时,需要给内存池指定一个名称。然后内核从系统中申请一个内存池对象,然后从内存堆中分配一块由块数目和块大小计算得来的内存缓冲区,接着初始化内存池对象,并将申请成功的内存缓冲区组织成可用于分配的空闲块链表。
函数参数
- 参数 描述
- name 内存池名;
- block_count 内存块数量;
- block_size 内存块容量。
函数返回
创建内存池对象成功,将返回内存池的句柄;否则返回RT_NULL。
删除内存池
删除内存池将删除内存池对象并释放申请的内存。使用下面的函数接口:
- rt_err_t rt_mp_delete(rt_mp_t mp);
删除内存池时,会首先唤醒等待在该内存池对象上的所有线程(返回-RT_ERROR),然后再释放已从内存堆上分配的内存池数据存放区域,然后删除内存池对象。
函数参数
- 参数 描述
- mp rt_mp_create返回的内存池对象句柄。
函数返回
返回RT_EOK
初始化内存池
初始化内存池跟创建内存池类似,只是初始化内存池用于静态内存管理模式,内存池控制块来源于用户在系统中申请的静态对象。另外与创建内存池不同的是,此处内存池对象所使用的内存空间是由用户指定的一个缓冲区空间,用户把缓冲区的指针传递给内存池对象控制块,其余的初始化工作与创建内存池相同。函数接口如下:
- rt_err_t rt_mp_init(rt_mp_t mp, const char* name, void *start, rt_size_t size, rt_size_t block size);
初始化内存池时,把需要进行初始化的内存池对象传递给内核,同时需要传递的还有内存池用到的内存空间,以及内存池管理的内存块数目和块大小,并且给内存池指定一个名称。这样,内核就可以对该内存池进行初始化,将内存池用到的内存空间组织成可用于分配的空闲块链表。
函数参数
- 参数 描述
- mp 内存池对象;
- name 内存池名;
- start 内存池的起始位置;
- size 内存池数据区域大小;
- block_size 内存块容量。
函数返回
初始化成功返回RT_OK;否则返回-RT_ERROR。
脱离内存池
脱离内存池将把内存池对象从内核对象管理器中删除。脱离内存池使用下面的函数接口:
- rt_err_t rt_mp_detach(rt_mp_t mp);
使用该函数接口后,内核先唤醒所有等待在该内存池对象上的线程,然后将内存池对象从内核对象管理器中删除。
函数参数
- 参数 描述
- mp 内存池对象。
函数返回
返回RT_EOK。
分配内存块
从指定的内存池中分配一个内存块,使用如下接口:
- void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time);
如果内存池中有可用的内存块,则从内存池的空闲块链表上取下一个内存块,减少空闲块数目并返回这个内存块;如果内存池中已经没有空闲内存块,则判断超时时间设置:若超时时间设置为零,则立刻返回空内存块;若等待时间大于零,则把当前线程挂起在该内存池对象上,直到内存池中有可用的自由内存块,或等待时间到达。
函数参数
- 参数 描述
- mp 内存池对象;
- time 超时时间。
函数返回
成功时返回分配的内存块地址,失败时返回RT_NULL。
释放内存块
任何内存块使用完后都必须被释放,否则会造成内存泄露,释放内存块使用如下接口:
- void rt_mp_free (void *block);
使用该函数接口时,首先通过需要被释放的内存块指针计算出该内存块所在的(或所属于的)内存池对象,然后增加内存池对象的可用内存块数目,并把该被释放的内存块加入空闲内存块链表上。接着判断该内存池对象上是否有挂起的线程,如果有,则唤醒挂起线程链表上的首线程。
函数参数
- 参数 描述
- block 内存块指针。
函数返回
无
内存池使用的例程如下所示:
- /*
- * 程序清单:内存池例程
- *
- * 这个程序会创建一个静态的内存池对象,2 个动态线程。
- * 两个线程会试图分别从内存池中获得内存块
- */
- #include <rtthread.h>
- #include "tc_comm.h"
- static rt_uint8_t *ptr[48];
- static rt_uint8_t mempool[4096];
- static struct rt_mempool mp; /* 静态内存池对象 */
- /* 指向线程控制块的指针 */
- static rt_thread_t tid1 = RT_NULL;
- static rt_thread_t tid2 = RT_NULL;
- /* 线程 1 入口 */
- static void thread1_entry(void* parameter)
- {
- int i;
- char *block;
- while(1)
- {
- for (i = 0; i < 48; i++)
- {
- /* 申请内存块 */
- rt_kprintf("allocate No.%d\n", i);
- if (ptr[i] == RT_NULL)
- {
- ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
- }
- }
- /* 继续申请一个内存块,因为已经没有内存块,线程应该被挂起 */
- block = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
- rt_kprintf("allocate the block mem\n");
- /* 释放这个内存块 */
- rt_mp_free(block);
- block = RT_NULL;
- }
- }
- /* 线程 2 入口,线程 2 的优先级比线程 1 低,应该线程 1 先获得执行。*/
- static void thread2_entry(void *parameter)
- {
- int i;
- while(1)
- {
- rt_kprintf("try to release block\n");
- for (i = 0 ; i < 48; i ++)
- {
- /* 释放所有分配成功的内存块 */
- if (ptr[i] != RT_NULL)
- {
- rt_kprintf("release block %d\n", i);
- rt_mp_free(ptr[i]);
- ptr[i] = RT_NULL;
- }
- }
- /* 休眠 10 个 OS Tick */
- rt_thread_delay(10);
- }
- }
- int mempool_simple_init()
- {
- int i;
- for (i = 0; i < 48; i ++) ptr[i] = RT_NULL;
- /* 初始化内存池对象 */
- rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80);
- /* 创建线程 1 */
- tid1 = rt_thread_create("t1",
- thread1_entry, /* 线程入口是 thread1_entry */
- RT_NULL, /* 入口参数是 RT_NULL */
- THREAD_STACK_SIZE, THREAD_PRIORITY,
- THREAD_TIMESLICE);
- if (tid1 != RT_NULL)
- rt_thread_startup(tid1);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- /* 创建线程 2 */
- tid2 = rt_thread_create("t2",
- thread2_entry, /* 线程入口是 thread2_entry */
- RT_NULL, /* 入口参数是 RT_NULL */
- THREAD_STACK_SIZE, THREAD_PRIORITY + 1,
- THREAD_TIMESLICE);
- if (tid2 != RT_NULL)
- rt_thread_startup(tid2);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- #ifdef RT_USING_TC
- static void _tc_cleanup()
- {
- /* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
- rt_enter_critical();
- /* 删除线程 */
- if (tid1 != RT_NULL && tid1->stat != RT_THREAD_CLOSE)
- rt_thread_delete(tid1);
- if (tid2 != RT_NULL && tid2->stat != RT_THREAD_CLOSE)
- rt_thread_delete(tid2);
- /* 执行内存池脱离 */
- rt_mp_detach(&mp);
- /* 调度器解锁 */
- rt_exit_critical();
- /* 设置 TestCase 状态 */
- tc_done(TC_STAT_PASSED);
- }
- int _tc_mempool_simple()
- {
- /* 设置 TestCase 清理回调函数 */
- tc_cleanup(_tc_cleanup);
- mempool_simple_init();
- /* 返回 TestCase 运行的最长时间 */
- return 100;
- }
- /* 输出函数命令到 finsh shell 中 */
- FINSH_FUNCTION_EXPORT(_tc_mempool_simple, a memory pool
- example);
- #else
- /* 用户应用入口 */
- int rt_application_init()
- {
- mempool_simple_init();
- return 0;
- }
- #endif