说明

alios thing(rhino内核)是rtos系统,不像Linux 有用户空间和内核空间的划分,内存的管理和使用都在同一个空间中,使用相对简单。项目使用平台,虚拟内存和物理内存配置成一样,内存操作和直接操作物理内存没太多差别。

使用

内存申请/释放函数

函数名描述aos_malloc()从系统heap分配内存给用户aos_zalloc()从系统heap分配内存给用户,并且将分配的内存初始化为0aos_calloc()从系统heap分配内存给用户,并且将分配的内存初始化为0aos_realloc()重新调整之前调用 aos_malloc(aos_calloc、aos_zalloc)所分配的内存块的大小aos_free()内存释放函数

以上是aos层接口,也可以使用POSIX接口(malloc/calloc/free等)和linux一样。alios thing层级划分:内核(rhino)+ aos(aos 接口层) + POSIX(可移植操作系统接口)。

内核实现

AliOS Things内存管理采用类buddy伙伴算法,以及blk快速小内存申请算法相结合的策略。

Buddy(伙伴系统)

待补充。

BLK(小内存快速申请算法)

源码:components/rhino/k_mm_blk.c头文件:components/rhino/include/k_mm_blk.h

为什么需要该算法

如果采用Buddy算法从大块内存中申请/释放小内存,可能会导致大块内存需要多次拆分和合并,此操作会造成性能浪费。小内存频繁申请释放的场景下,可能导致内存碎片。

源码解析

接口说明:

// Init a pool. 创建内存pool

kstat_t krhino_mblk_pool_init(mblk_pool_t *pool, const name_t *name,

void *pool_start, size_t pool_size);

// Memory block alloc from the pool. 从pool中申请内存

void *krhino_mblk_alloc(mblk_pool_t *pool, uint32_t size); //已加锁

void *krhino_mblk_alloc_nolock(mblk_pool_t *pool, uint32_t size); //未加锁

// Memory block free to the pool. 释放pool中的内存

kstat_t krhino_mblk_free(mblk_pool_t *pool, void *blk); //已加锁

kstat_t krhino_mblk_free_nolock(mblk_pool_t *pool, void *blk); //未加锁

// This function will get information of the pool 获取pool信息

kstat_t krhino_mblk_info(mblk_pool_t *pool, mblk_info_t *info); //已加锁

kstat_t krhino_mblk_info_nolock(mblk_pool_t *pool, mblk_info_t *info); //未加锁

// Check if this a pool block. 判断内存是从pool中申请的

#define krhino_mblk_check(pool, blk) \

((pool) != NULL \

&& ((uintptr_t)(blk) >= ((mblk_pool_t*)(pool))->pool_start) \

&& ((uintptr_t)(blk) < ((mblk_pool_t*)(pool))->pool_end))

// get blk size, should followed by krhino_mblk_check 获取内存大小,需要先确保内存是从pool中申请的

RHINO_INLINE size_t krhino_mblk_get_size(mblk_pool_t *pool, void *blk)

Pool 初始化

kstat_t krhino_init_mm_head(k_mm_head **ppmmhead, void *addr, size_t len)

{

....

#if (RHINO_CONFIG_MM_BLK > 0)

pmmhead->fix_pool = NULL;

mmblk_pool = k_mm_alloc(pmmhead, RHINO_CONFIG_MM_TLF_BLK_SIZE + MM_ALIGN_UP(sizeof(mblk_pool_t)));

if (mmblk_pool) {

stat = krhino_mblk_pool_init(mmblk_pool, "fixed_mm_blk",

(void *)((size_t)mmblk_pool + MM_ALIGN_UP(sizeof(mblk_pool_t))),

RHINO_CONFIG_MM_TLF_BLK_SIZE);

if (stat == RHINO_SUCCESS) {

pmmhead->fix_pool = mmblk_pool;

} else {

k_mm_free(pmmhead, mmblk_pool);

}

}

#endif

return RHINO_SUCCESS;

}

堆内存区域初始化后,申请一块固定大小内存,用来初始化pool。

内存申请

void *k_mm_alloc(k_mm_head *mmhead, size_t size)

{

....

#if (RHINO_CONFIG_MM_BLK > 0)

/* little blk, try to get from mm_pool */

if (mmhead->fix_pool != NULL && size <= RHINO_CONFIG_MM_BLK_SIZE) {

retptr = krhino_mblk_alloc_nolock((mblk_pool_t *)mmhead->fix_pool, size);

if (retptr) {

MM_CRITICAL_EXIT(mmhead, flags_cpsr);

return retptr;

}

}

#endif

....

}

每次申请内存,若申请的size小于或等于阈值时,先从pool中申请,申请失败再使用Buddy算法申请。

内存释放

void k_mm_free(k_mm_head *mmhead, void *ptr)

{

....

#if (RHINO_CONFIG_MM_BLK > 0)

if (krhino_mblk_check(mmhead->fix_pool, ptr)) {

(void)krhino_mblk_free_nolock((mblk_pool_t *)mmhead->fix_pool, ptr);

MM_CRITICAL_EXIT(mmhead, flags_cpsr);

return;

}

#endif

...

}

每次释放内存,先判断内存地址是否在pool内,在的话,调用pool内存释放接口。

核心函数

pool内存申请函数

void *krhino_mblk_alloc_nolock(mblk_pool_t *pool, uint32_t size)

{

uint32_t blk_type;

mblk_list_t *blk_list = NULL;

uintptr_t avail_blk = (uintptr_t)NULL;

if (pool == NULL) {

return NULL;

}

size = size < sizeof(uintptr_t) ? sizeof(uintptr_t) : size; //申请block大小至少是4字节(指针大小)

blk_type = MM_BLK_SIZE2TYPE(size);

while (blk_type < MM_BLK_SLICE_BIT) {

blk_list = &(pool->blk_list[blk_type]);

/* try to get from freelist */

if ((avail_blk = blk_list->free_head) != (uintptr_t)NULL) {

blk_list->free_head = *(uintptr_t *)avail_blk;

blk_list->freelist_cnt--;

break;

}

/* check if need new slice */

if (blk_list->slice_addr == 0 || blk_list->slice_offset == MM_BLK_SLICE_SIZE) {

if (pool->slice_cnt == MM_BLK_SLICE_NUM) {

blk_type++;

continue;

}

/* get new slice for this type blks */

blk_list->slice_addr = pool->pool_start + pool->slice_cnt * MM_BLK_SLICE_SIZE;

pool->slice_type[pool->slice_cnt] = blk_type;

blk_list->slice_offset = 0;

pool->slice_cnt++;

blk_list->slice_cnt++;

}

/* cut blk from slice */

avail_blk = blk_list->slice_addr + blk_list->slice_offset;

blk_list->slice_offset += blk_list->blk_size;

break;

};

if (blk_list) {

(avail_blk == (uintptr_t)0) ? blk_list->fail_cnt++ : blk_list->nofree_cnt++;

}

return (void *)avail_blk;

}

描述:pool初始化时会申请一块区域,并平均划分为多个小块,一小块称为一个切片(slice),算法使用数组保存各个blk_type的数据,blk_type对应的内存大小是位移生成的(1 << blk_type), 1,2,4,8,16,32,64,128…字节, 而切片大小是最大size的两倍。

pool内存释放函数

kstat_t krhino_mblk_free_nolock(mblk_pool_t *pool, void *blk)

{

uint32_t slice_idx;

uint32_t blk_type;

mblk_list_t *blk_list;

NULL_PARA_CHK(pool);

NULL_PARA_CHK(blk);

slice_idx = ((uintptr_t)blk - pool->pool_start) >> MM_BLK_SLICE_BIT;

if (slice_idx >= MM_BLK_SLICE_NUM) {

return RHINO_MM_FREE_ADDR_ERR;

}

blk_type = pool->slice_type[slice_idx];

if (blk_type >= MM_BLK_SLICE_BIT) {

return RHINO_MM_FREE_ADDR_ERR;

}

blk_list = &(pool->blk_list[blk_type]);

/* use the first 4 byte of the free block point to head of free list */

*((uintptr_t *)blk) = blk_list->free_head;

blk_list->free_head = (uintptr_t)blk;

blk_list->nofree_cnt--;

blk_list->freelist_cnt++;

return RHINO_SUCCESS;

}

freelist(空闲链表)的实现方式是: 在每个节点前4个字节保存下个节点的内存地址;内存释放时只需要在保存freelist首节点的内存地址到待释放内存中,并将待释放内存插入到链表首。

算法特征

内存重复使用内存申请阈值,小于该值的内存申请先采用该算法,申请失败或者大于该阈值的内存申请采用Buddy算法。内存申请时不涉及拆分,内存释放时,直接挂在到对应type的空闲链表内,释放不涉及合并。算法性能较高,但是不可靠,由于不涉及到合并,当切片使用完后,其分配情况就固定了,可能出现所有的切片被少数几个blk_type使用完了,再分配其它大小的小内存则会申请失败。

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。