RT-Thread源码-8-内存池剖析

前言
  • 在C中,可以通过malloc、calloc、realloc进行动态的、任意大小的内存块申请,以及通过free进行内存块释放;在C++中,能够通过new及delete执行上述操作。这样的优点非常明显,它们使内存块的申请分配变得非常灵活方便。但是,由于它的灵活性,将会有如下缺点:
    1. 由于内存本身是连续的,在任意申请又释放达到一定次数后,在没有内存管理的情况下,它难免会出现内存碎片;
    2. 在内存操作过程中,若非原子操作,在多线程环境下将容易出现线程安全问题;
  • RT-Thread中提供了内存池的管理方法。它能够通过链表静态存储的方式(链表管理便于增加及删除,方便管理,但是遍历效率低),存储大量大小相同的小内存块,能够加快分配和释放时的效率,避免内存碎片的产生。并且,它是线程安全的,在内存分配时,若无可用内存块,申请线程将会挂起等待新释放的内存块。虽然它在每次分配时仍是需要遍历链表,效率不是非常高,但是能避免因为无内存管理而产生的性能问题。
数据结构
struct rt_mempool
{
    struct rt_object parent;                            /**< 父对象节点 */

    void            *start_address;                     /**< 内存池起始地址 */
    rt_size_t        size;                              /**< 内存池大小 */

    rt_size_t        block_size;                        /**< 单个内存块大小 */
    rt_uint8_t      *block_list;                        /**< 内存块链表。在初始化时将内存池平分而得 */

    rt_size_t        block_total_count;                 /**< 内存块总数*/
    rt_size_t        block_free_count;                  /**< 空闲内存块数 */

    rt_list_t        suspend_thread;                    /**< 等待内存块的挂起线程表 */
    rt_size_t        suspend_thread_count;              /**< 等待内存块的挂起线程数 */
};
typedef struct rt_mempool *rt_mp_t;
创建
  • RT-Thread中内存池的创建同其他对象类似。创建分为静态创建及动态创建。
    1. 静态创建由用户提供内存块,系统将其分为大小相同的小内存块,并且将它们连接为内存块链表。这里指向每个内存块的都是无符号字符型指针;
    2. 动态创建与静态创建类似,不同的是它的内存池对象及内存池是从堆上申请的,内存池大小是通过用户提供的单个内存块大小及内存块数量而定。
  • 以下为源码:
rt_err_t rt_mp_init(struct rt_mempool *mp,
                    const char        *name,
                    void              *start,
                    rt_size_t          size,
                    rt_size_t          block_size)
{
    rt_uint8_t *block_ptr;
    register rt_size_t offset;

    /* parameter check */
    RT_ASSERT(mp != RT_NULL);

    /* initialize object */
    rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);

    /* initialize memory pool */
    mp->start_address = start;
    mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);

    /* align the block size */
    block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
    mp->block_size = block_size;

    /* align to align size byte */
    mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
    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 *))) =
            (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 RT_EOK;
}

rt_mp_t rt_mp_create(const char *name,
                     rt_size_t   block_count,
                     rt_size_t   block_size)
{
    ...

    /* 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;
    }

    ...

    return mp;
}
删除
  • 关于内存池的删除函数,它的主要工作是将删除前正在等待内存块资源的挂起线程依个唤醒。静态创建和动态创建在删除时的区别这里不多做赘述。
分配
  • 直接贴出源码:
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 *));
}
  • 若内存池中无空闲线程块,会将调用该函数的线程挂起,直到有新的空闲内存块可用或达到挂起超时时间为止;反之,则将空闲内存块数量减1之后分配出一个空闲内存块返回。
释放
  • 释放与分配执行的操作相反。这里首先将空闲内存块计数加1,之后将传入的释放内存块放入内存块链表,最后若存在等待空闲内存块的挂起线程,则唤醒,返回。
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);
}
其余
  • 在分配和释放过程中,每次除了待操作的内存块外,还有一个内存池控制块。这些在初始化创建时都是已经固定的,“浓缩”进了空闲内存块计数及创建时的内存对齐中,因此在除内存池创建及删除的操作中不需要担心每次申请分配的内存块会超出内存池的大小或在释放时出现重复释放的问题。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值