源码解读·RT-Thread操作系统内存管理之内存池
Posted 鹏城码夫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码解读·RT-Thread操作系统内存管理之内存池相关的知识,希望对你有一定的参考价值。
内存管理管理算法中内存池与前面分析的小内存管理算法不同,内存池通常用于已知大小的、高可靠性、高时效性的地方,一般常见于缓冲区。内存池管理算法的特性是内存池中的内存块大小已知,且内存块总数也已知,所以不会造成内存碎片,其次只要分配的内存块数量不超过有限值,分配内存块的时间复杂度是O(1)。
本章所涉及的内容如下:
1. 内存池的原型
2. 内存池的创建rt_mp_create
3. 内存池的分配rt_mp_alloc
4. 内存池的释放rt_mp_free
/**
* 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;
原型字段注释:
parent:系统对象,初始化时会被初始化分配为一个RT_Object_Class_MemPool对象。
size:内存池的总大小
block_size:内存池分配的内存块的大小
block_list:内存池空闲内存块指针
block_total_count:内存池总共的内存块数目
block_free_count:内存池剩余的空闲内存块数目
suspend_thread:等待分配内存块而挂起的任务等待队列
suspend_thread_count:等待队列任务总数
/**
* This function will create a mempool object and allocate the memory pool from
* heap.
*
* @param name the name of memory pool
* @param block_count the count of blocks in memory pool
* @param block_size the size for each block
*
* @return the created mempool object
*/
rt_mp_t rt_mp_create(const char *name,
rt_size_t block_count,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
struct rt_mempool *mp;
register rt_size_t offset;
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate object */
mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
/* allocate object failed */
if (mp == RT_NULL)
return RT_NULL;
/* initialize memory pool */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
/* allocate memory */
mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
block_count);
if (mp->start_address == RT_NULL)
{
/* no memory, delete memory pool object */
rt_object_delete(&(mp->parent));
return RT_NULL;
}
mp->block_total_count = block_count;
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
mp->suspend_thread_count = 0;
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
= block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
= RT_NULL;
mp->block_list = block_ptr;
return mp;
}
函数参数注释:
name:内存池在系统对象列表中的名字
block_count:内存池中内存块的数量
block_size:内存池中内存块的大小
内存池是由多个相同大小的内存块组成,所以参数中指定了内存池的内存块数量和内存块大小。函数中首先分配一个RT_Object_Class_MemPool类型的系统对象,也就是内存池对象。然后对内存池的各个字段进行赋值初始化,其中size字段代表内存池占用的总共的内存字节大小,而由于内存块与内存块需要通过一个指针来连接,所以每个内存块的最前面需要一个指针,因而size字段就是(block_size + sizeof(rt_uint8_t *)) * block_count。然后通过其它的内存管理算法动态分配一块空间用来做内存池的空间。接着对其它字段进行相应初始化。
函数关键部分是下部分的for循环,这个循环依次将start_address指向的内存池空间,按照block_size + sizeof(rt_uint8_t *)大小进行切割链接成一个单向链表。类似于下图:
最后block_list指向内存池的最前面,此时也是最前面的空闲块。
/**
* This function will allocate a block from memory pool
*
* @param mp the memory pool object
* @param time the waiting time
*
* @return the allocated memory block or RT_NULL on allocated failed
*/
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
{
rt_uint8_t *block_ptr;
register rt_base_t level;
struct rt_thread *thread;
rt_uint32_t before_sleep = 0;
/* get current thread */
thread = rt_thread_self();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (mp->block_free_count == 0)
{
/* memory block is unavailable. */
if (time == 0)
{
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_set_errno(-RT_ETIMEOUT);
return RT_NULL;
}
RT_DEBUG_NOT_IN_INTERRUPT;
thread->error = RT_EOK;
/* need suspend thread */
rt_thread_suspend(thread);
rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist));
mp->suspend_thread_count++;
if (time > 0)
{
/* get the start tick of timer */
before_sleep = rt_tick_get();
/* init thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&time);
rt_timer_start(&(thread->thread_timer));
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* do a schedule */
rt_schedule();
if (thread->error != RT_EOK)
return RT_NULL;
if (time > 0)
{
time -= rt_tick_get() - before_sleep;
if (time < 0)
time = 0;
}
/* disable interrupt */
level = rt_hw_interrupt_disable();
}
/* memory block is available. decrease the free block counter */
mp->block_free_count--;
/* get block from block list */
block_ptr = mp->block_list;
RT_ASSERT(block_ptr != RT_NULL);
/* Setup the next free node. */
mp->block_list = *(rt_uint8_t **)block_ptr;
/* point to memory pool */
*(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
(mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
}
内存池的分配,支持等待操作,如果暂时没有空闲内存块可以分配,那么任务可以选择等待挂起。超时或者唤醒后重新检查是否有空闲块可以分配,如果有空闲块可以分配,则直接从block_list指向的空闲块链表头取出一个内存块。
函数中首先检查block_free_count是否大于0,不大于0,则进入循环判断是否需要挂起,不挂起则返回NULL。否则将任务挂起,并插入到suspend_list链表中,同时对suspend_thread_count进行加1,并在启动任务定时器进行超时计时后,主动触发调度器进行调度。此后任务要么被内存池释放的地方唤醒,要么被定时器超时唤醒。所以唤醒后重新检查一下剩余的等待时间,并检查是否有空闲内存块可用。等待超时后会返回NULL。否则要么继续等待,要么有可用内存块可分配,那么退出while循环开始分配内存块。
/**
* This function will release a memory block
*
* @param block the address of memory block to be released
*/
void rt_mp_free(void *block)
{
rt_uint8_t **block_ptr;
struct rt_mempool *mp;
struct rt_thread *thread;
register rt_base_t level;
/* get the control block of pool which the block belongs to */
block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
mp = (struct rt_mempool *)*block_ptr;
RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* increase the free block count */
mp->block_free_count ++;
/* link the block into the block list */
*block_ptr = mp->block_list;
mp->block_list = (rt_uint8_t *)block_ptr;
if (mp->suspend_thread_count > 0)
{
/* get the suspended thread */
thread = rt_list_entry(mp->suspend_thread.next,
struct rt_thread,
tlist);
/* set error */
thread->error = RT_EOK;
/* resume thread */
rt_thread_resume(thread);
/* decrease suspended thread count */
mp->suspend_thread_count --;
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* do a schedule */
rt_schedule();
return;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
最后检查任务等待链表中是否有任务处于等待,有则唤醒等待链表的头部任务。最后触发调度器调度后函数返回。
在这个任务等待链表的处理方式上应该是有背实时系统的理念的。因为整个等待队列是按照fifo形式进行的,并非按照任务优先级的优先级队列进行的,所以在实时系统中应该要把一切任务等待队列改成优先级队列。
以上是关于源码解读·RT-Thread操作系统内存管理之内存池的主要内容,如果未能解决你的问题,请参考以下文章