内存池的使用
内存池介绍
动态内存堆可以分配任意大小的内存块,非常灵活和方便。但其存在明显的缺点:一是分配效率不高,在每次分配时,都要进行空闲内存块查找;二是容易产生内存碎片。
为了提高内存分配的效率,并且避免内存碎片,RT-Thread 提供了另外一种内存管理方法:内存池(Memory Pool)
内存池是一种内存分配方式,用于分配大量大小相同的小内存块。使用内存池可以极大地加快内存分配与释放的速度,且能尽量避免内存碎片化。
RT-Thread的内存池支持线程挂起功能,当内存池中无空闲内存块时,申请线程会被挂起,直到内存池中有新的可用内存块,再将挂起的线程唤醒。基于这个特点内存池非常适合需要通过内存资源进行同步的场景。
内存池工作机制
内存池在创建时先从系统中获取一大块内存 (静态或动态),然后分成相同大小的多个小内存块,这些小内存块通过链表连接起来( 此链表也称为空闲链表)。线程每次申请分配内存块的时候,系统从空闲链表中取出链头上第一个内存块,提供给申请者。
内存池控制块
在RT-Thread中,内存池控制块是操作系统用于管理内存池的一个数据结构。
/* 在rtdef.h中对结构体的定义 */
#ifdef RT_USING_MEMPOOL
/**
* Base structure of Memory pool object
*/
struct rt_mempool
{
struct rt_object parent; /**< inherit from rt_object */
//内存池起始地址 大小
void *start_address; /**< memory pool start */
rt_size_t size; /**< size of memory pool */
//内存块大小 内存块列表
rt_size_t block_size; /**< size of memory blocks */
rt_uint8_t *block_list; /**< memory blocks list */
//内存块总数 空闲内存块数量
rt_size_t block_total_count; /**< numbers of memory block */
rt_size_t block_free_count; /**< numbers of free memory block */
//挂起在内存池的线程列表 及其 数量
rt_list_t suspend_thread; /**< threads pended on this resource */
rt_size_t suspend_thread_count; /**< numbers of thread pended on this resource */
};
typedef struct rt_mempool *rt_mp_t;
#endif
struct rt_mempool static_mp //定义静态邮箱
rt_mp_t dynamic_mp //定义动态信号量
内存池的操作
初始化与脱离
//用于静态内存池
rt_err_t rt_mp_init(rt_mempool *mp, const char *name, void *start, rt_size_t size, rt_size_t block_size)
/* 内存块数目计算公式:block_count = size/(block_size(4的整数倍) + 4(指针大小)) */
rt_err_t rt_mp_detach(rt_mp_t mp)
创建和删除
//用于动态内存池
rt_mp_t rt_mp_create(const char *name, t_size_t block_count, rt_size_t block_size)
rt_err_t rt_mp_delete(rt_mp_t mp)
申请内存块
rt_err_t rt_mp_alloc(rt_mp_t mp, rt_uint32_t time);
释放内存块
rt_err_t rt_mp_free(void *block)
小例
代码
/*
* 程序清单:内存池例程
*
* 这个程序会创建一个静态的内存池对象,2个动态线程。
* 一个线程会试图从内存池中获得内存块,另一个线程释放内存块
*/
#include <rtthread.h>
static rt_uint8_t *ptr[50];
static rt_uint8_t mempool[4096];
static struct rt_mempool mp;
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
/* 线程1入口 */
static void thread1_mp_alloc(void *parameter)
{
int i;
for (i = 0 ; i < 50 ; i++)
{
if (ptr[i] == RT_NULL)
{
/* 试图申请内存块50次,当申请不到内存块时,线程1挂起,转至线程2运行 */
ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
if (ptr[i] != RT_NULL)
rt_kprintf("allocate No.%d\n", i);
}
}
}
/* 线程2入口,线程2的优先级比线程1低,应该线程1先获得执行。*/
static void thread2_mp_release(void *parameter)
{
int i;
rt_kprintf("thread2 try to release block\n");
for (i = 0; i < 50 ; i++)
{
/* 释放所有分配成功的内存块 */
if (ptr[i] != RT_NULL)
{
rt_kprintf("release block %d\n", i);
rt_mp_free(ptr[i]);
ptr[i] = RT_NULL;
}
}
}
int mempool_sample(void)
{
int i;
for (i = 0; i < 50; i ++) ptr[i] = RT_NULL;
/* 初始化内存池对象 */
/* block_count = 4096 / (80 + 4) = 48 */
rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80);
/* 创建线程1:申请内存池 */
tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* 创建线程2:释放内存池*/
tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY + 1, THREAD_TIMESLICE);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mempool_sample, mempool sample);