2021SC@SDUSC
静态内存池就是将一块内存划分为n个大小相等的块,用户可以动态的申请、释放一个块,表面上的体现就是在使用动态内存。
内存池(Memory Pool)是一种用于分配大量大小相同的小对象的技术。它可以极大加快内存分配/释放的速度。
内存池在创建时先向系统申请一大块内存,然后分成同样大小的多个小内存块,小内存块直接通过链表连接起来(此链表也称为空闲链表,和TLSF中的空闲块链表类似)。每次分配的时候,从空闲链表中取出链头上第一个内存块,提供给申请者。物理内存中允许存在多个大小不同的内存池,每一个内存池又由多个空闲内存块组成,内核用它们来进行内存管理。
内核是如何管理内存池的呢?当一个内存池对象被创建时,内存池对象就被分配给了一个内存池控制块,内存控制块的参数包括内存池名,内存缓冲区,内存块大小,块数以及一个等待线程队列。
内核负责给内存池分配内存池对象控制块,它同时也接收用户线程的分配内存块申请,当获得这些信息后,内核就可以从内存池中为内存池分配内存。内存池一旦初始化完成,内部的内存块大小将不能再做调整。
这个算法的基本思想还是比起TLSF更显简单,但是其仍然具有广泛的应用和重要性,下面我们直接来看TencentOS Tiny中是如何实现静态内存池管理算法的:
在TencentOS Tiny中关于静态内存池管理,一共有四个基本的API,分别是内存池的创建、销毁,从内存池中申请、放回一个空闲块;以及两个更高级的API,分别是内存池的动态创建和销毁,这里我们只分析四个基础API的实现。
TencentOS-tiny中静态内存池管理算法的实现在tos_mmblk.h
和tos_mmblk.c
中。我们首先来看内存池的创建
__API__ k_err_t tos_mmblk_pool_create(k_mmblk_pool_t *mbp, void *pool_start, size_t blk_num, size_t blk_size)
{
uint32_t i;
void *blk_curr, *blk_next;
TOS_IN_IRQ_CHECK();
TOS_PTR_SANITY_CHECK(pool_start);
if (((cpu_addr_t)pool_start & K_MMBLK_ALIGN_MASK) != 0u) {
return K_ERR_MMBLK_INVALID_POOL_ADDR;
}
if ((blk_size & K_MMBLK_ALIGN_MASK) != 0u) {
return K_ERR_MMBLK_INVALID_BLK_SIZE;
}
blk_curr = pool_start;
blk_next = K_MMBLK_NEXT_BLK(blk_curr, blk_size);
for (i = 0; i < blk_num - 1u; ++i) {
*(void **)blk_curr = blk_next;
blk_curr = blk_next;
blk_next = K_MMBLK_NEXT_BLK(blk_next, blk_size);
}
*(void **)blk_curr = K_NULL;
mbp->pool_start = pool_start;
mbp->free_list = pool_start;
mbp->blk_free = blk_num;
mbp->blk_max = blk_num;
mbp->blk_size = blk_size;
TOS_OBJ_INIT(mbp, KNL_OBJ_TYPE_MMBLK_POOL);
knl_object_alloc_set_static(&mbp->knl_obj);
return K_ERR_NONE;
}
这个创建内存池的函数通过传入一个用来指向这个即将被创建的内存池的指针mbp
和内存池的起始地址pool_start
,内存池中内存块的数量blk_num
,单个内存块的大小blk_size
来创建内存池,系统可以自动的根据传入的这些参数来进行计算后给内存池分配空间,而不需要使用参数指定空间的具体大小,这样可以省掉一个参数。
然后来看一下实际的创建过程,整个创建过程关键部分是利用pool_start
、free_list
、blk_free
、blk_max
、blk_size
等各参数在初始化mbp指针所指向的内存池对象,最后再调用knl_object_alloc_set_static
函数给内存池分配内存空间
__API__ k_err_t tos_mmblk_pool_destroy(k_mmblk_pool_t *mbp)
{
TOS_PTR_SANITY_CHECK(mbp);
TOS_OBJ_VERIFY(mbp, KNL_OBJ_TYPE_MMBLK_POOL);
#if TOS_CFG_MMHEAP_EN > 0u
if (!knl_object_alloc_is_static(&mbp->knl_obj)) {
return K_ERR_OBJ_INVALID_ALLOC_TYPE;
}
#endif
mbp->pool_start = K_NULL;
mbp->free_list = K_NULL;
mbp->blk_free = 0;
mbp->blk_max = 0;
mbp->blk_size = 0;
TOS_OBJ_DEINIT(mbp);
return K_ERR_NONE;
}
销毁内存池就比较简单了,首先利用mbp指针或者叫做句柄来设置所指向的内存池对象的参数列表信息,然后调用TOS_OBJ_DEINIT释放资源即可
__API__ k_err_t tos_mmblk_alloc(k_mmblk_pool_t *mbp, void **blk)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(mbp);
TOS_OBJ_VERIFY(mbp, KNL_OBJ_TYPE_MMBLK_POOL);
TOS_CPU_INT_DISABLE();
if (mbp->blk_free == 0) {
TOS_CPU_INT_ENABLE();
*blk = K_NULL;
return K_ERR_MMBLK_POOL_EMPTY;
}
*blk = mbp->free_list;
mbp->free_list = *(void **)mbp->free_list;
--mbp->blk_free;
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
从内存池中获取内存块时,首先调用TOS_CPU_INT_DISABLE
保留CPU寄存器中的状态字然后关中断,使CPU执行下面的语句,如果内存池句柄mpb指向的内存池剩余空间参数为0,表示目前内存池无可分配的空闲内存块,那么就开中断,然后返回错误信息K_ERR_MMBLK_POOL_EMPTY
表示内存池已空,如果内存池有剩余可分配空闲内存块,则首先将内存池中的内存块通过为参数blk指针赋值来分配出去然后通过mbp设置内存池对象的各项参数,完成之后就开中断返回成功执行信息
__API__ k_err_t tos_mmblk_free(k_mmblk_pool_t *mbp, void *blk)
{
TOS_CPU_CPSR_ALLOC();
TOS_PTR_SANITY_CHECK(mbp);
TOS_PTR_SANITY_CHECK(blk);
TOS_OBJ_VERIFY(mbp, KNL_OBJ_TYPE_MMBLK_POOL);
TOS_CPU_INT_DISABLE();
if (mbp->blk_free >= mbp->blk_max) {
TOS_CPU_INT_ENABLE();
return K_ERR_MMBLK_POOL_FULL;
}
*(void **)blk = mbp->free_list;
mbp->free_list = blk;
++mbp->blk_free;
TOS_CPU_INT_ENABLE();
return K_ERR_NONE;
}
将内存块资源放回到内存池中和从内存池中取内存块的具体实现大同小异,首先也是通过调用TOS_CPU_INT_DISABLE
保留CPU寄存器中的状态字然后关中断,使CPU执行下面的语句,不同的便是下面这里,首先要通过判断mbp->blk_free >= mbp->blk_max
是否成立,如果成立则表示目前内存池没有被使用的空闲块,所有内存块均空闲,不再接收内存块资源的放入,然后就开中断,返回内存池已满的信息K_ERR_MMBLK_POOL_FULL
,如果内存池仍有资源未释放回来,则通过blk和mbp指针将资源释放回来,完成以后就开中断,返回成功执行信息。
以上就是本周的所有工作内容了,关于TencentOS Tiny的源码分析工作也到此结束,在本次课程内容中,我一共写了13篇相关博客,分别对TencentOS tiny 的进程调度、调度器、队列、消息队列、内存管理等模块进行了知识学习和源码分析。下面简要描述一下对于我来说一篇博客的成长历程
最后,很感谢学校能安排本次源码分析课程,让我有了很大的提升!