alios thing - rhino内核 - 内存管理

说明

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

使用

  • 内存申请/释放函数
函数名描述
aos_malloc()从系统heap分配内存给用户
aos_zalloc()从系统heap分配内存给用户,并且将分配的内存初始化为0
aos_calloc()从系统heap分配内存给用户,并且将分配的内存初始化为0
aos_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

为什么需要该算法

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

源码解析

  • 接口说明:
// 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)
  1. 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。
  1. 内存申请
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算法申请。
  1. 内存释放
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内存释放接口。
核心函数
  1. 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的两倍。
    在这里插入图片描述
    在这里插入图片描述
  1. 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首节点的内存地址到待释放内存中,并将待释放内存插入到链表首。

算法特征

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值