RT-Thread移植到S5P4418(三):静态内存池管理

内存堆管理器可以分配任意大小的内存块,非常灵活和方便。但其也存在明显的缺点:一是分配效率不高,在每次分配时,都要空闲内存块查找;二是容易产生内存碎片。为了提高内存分配的效率,并且避免内存碎片,RT-Thread 提供了另外一种内存管理方法:内存池(Memory Pool)。

内存池是一种内存分配方式,用于分配大量大小相同的小内存块,它可以极大地加快内存分配与释放的速度,且能尽量避免内存碎片化。此外,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_err_t rt_mp_init(rt_mp_t mp, const char *name, void *start, rt_size_t size, rt_size_t block_size){
    rt_ubase_t offset;

    rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name); // 对象初始化

    mp->start_address = start;

    size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
    mp->size = size;

    block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
    mp->block_size = block_size;

    mp->block_free_count = size / (block_size + sizeof(rt_ubase_t *));
    mp->block_total_count = mp->block_free_count;

    rt_list_init(&(mp->suspend_thread));
    mp->suspend_thread_count = 0;

    /* 形成单链表 */
    for(offset = 0; offset < mp->block_total_count - 1; offset++){
        *(rt_ubase_t **)((rt_uint8_t *)start + (block_size + sizeof(rt_ubase_t *)) * offset) = 
            (rt_ubase_t *)((rt_uint8_t *)start + (block_size + sizeof(rt_ubase_t *)) * (offset + 1));
    }
    *(rt_ubase_t **)((rt_uint8_t *)start + (block_size + sizeof(rt_ubase_t *)) * offset) = RT_NULL;

    mp->block_list = (rt_uint8_t *)start;

    return RT_EOK;
}

初始化后的内存分布:
在这里插入图片描述


申请内存块

void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time){
    rt_thread_t thread;
    rt_uint8_t *block_ptr;
    rt_ubase_t tmp;
    rt_uint32_t before_sleep = 0;

    thread = rt_thread_self();

    tmp = rt_hw_interrupt_disable();

    while(mp->block_free_count == 0){
        if(time == 0){  // 无等待的直接返回
            thread->error = -RT_ETIMEOUT;
            rt_hw_interrupt_enable(tmp);

            return RT_NULL;
        }

        thread->error = RT_EOK;

        rt_thread_suspend(thread);  // 等待的挂起线程
        rt_list_insert_before(&(mp->suspend_thread), &(thread->tlist));
        mp->suspend_thread_count++;

        if(time > 0){
            before_sleep = rt_tick_get();

            rt_timer_control(&thread->thread_timer, RT_TIMER_CTRL_SET_TIME, &time);
            rt_timer_start(&thread->thread_timer);  // 启动定时器
        }

        rt_hw_interrupt_enable(tmp);

        rt_schedule();  // 调度

        /* 超时或主动恢复线程 */
        if(thread->error != RT_EOK){
            return RT_NULL;
        }

        if(time > 0){
            time = rt_tick_get() - before_sleep;    // 未分配内存块,且未超时继续等待
            if(time < 0){
                time = 0;
            }
        }

        tmp = rt_hw_interrupt_disable();
    }
	
	/* 执行到这说明有空闲块了 */
    block_ptr = mp->block_list;
    mp->block_list = *(rt_uint8_t **)block_ptr;     // .block_list总是指向下一个空闲块
    *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;

    mp->block_free_count--;

    rt_hw_interrupt_enable(tmp);

    return (void *)(block_ptr + sizeof(rt_uint8_t *));
}

假如申请了两块内存。内存分布:
在这里插入图片描述


内存释放

void rt_mp_free(void *block){
    rt_uint8_t *block_ptr;
    rt_mp_t mp;
    rt_thread_t thread;
    rt_ubase_t tmp;

    RT_ASSERT(block != RT_NULL);

    block_ptr = (rt_uint8_t *)((rt_uint32_t)block - sizeof(rt_uint8_t *));
    mp = (rt_mp_t)(*(rt_uint8_t **)block_ptr);  // 获取该内存池控制块

    tmp = rt_hw_interrupt_disable();

    *(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp->block_list;   // .next
    mp->block_list = block_ptr; // 本块又变成了链表中的第一块空闲块

    mp->block_free_count++;

    if(mp->suspend_thread_count > 0){   // 判断是否有线程等待分配
        thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist);
        rt_thread_resume(thread);

        thread->error = RT_EOK;

        mp->suspend_thread_count--;

        rt_hw_interrupt_enable(tmp);

        rt_schedule();

        return ;
    }

    rt_hw_interrupt_enable(tmp);
}

将上面申请的两块内存释放,链表又恢复初始状态。


对于动态创建的内存池,只是多了一步申请内存的操作。


工程文件

rtt2a9_mempool

按键1申请,按键2释放

下载时请看下面说明,对写一个动态的内存池很有帮助。 这是一个用C++语言链表的方法实现的一个静态内存池代源码。原理就是先向系统申请一内存,然后把这一大分隔成相等的很多小,然后在这这些小的首地址部份放一个结构体,结构体中有一个值是用来标明这一小是否使用中。在这里存放你要放存的数据就是用结构体首地址加结构体自身长度就得到要放数据的首地址了.具体看代码的实现吧。我说一下动态内存池的写法。那是我给公司写的就不能上传了。结构体和静态内存池的这个差不多一样,只是增加了一个成员用来记录每一节点到大内存的首地址在到本节点的一个尺寸长度值,做法也是先申请一内存。我先从释放说起吧,释放本节点时看自己的相邻节点是不是有释放掉的,如果有则合并掉他们成为一个,如果碰到相邻的节点是另外的一个大的话就不用合并了,原因他和自己所在的这一个大内存上物理地址不是连续,这里一定要记住,释放过程算法怎么去写就看你的了。下面是分配写法要考虑的。在分配一小内存给高层使用时,如果是分配在尾节点去分配的情况,那好办啊,尾节点如果不够分配了就直接从系统去申请一内存,节点连起来在分配,这里有可能会浪费掉一小以结构体大小的一内存,如果够分配就直接分配了。如果是在中间节点去分配,这里就要将释放时合并的如果大于现在要分配的就拆开来用,如果拆开剩余的那一部份只有结构体大小就不用在拆开了。这些都是要考虑的东西,优化加快速度就看你自己了.可能看时不些不明白,看静态内存的写法后你就明白了.有时我也要下载其他人共享的东西,所以就一分吧.哈哈~~~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值