基于内存池的动态内存分配(malloc)

源代码是原子哥的内存池动态内存分配,实际使用中发现分配速度不够快,因此优化了一下分配算法。

在malloc.c中有两种分配的方式,屏蔽的那种在原子哥原有的基础上增加了内存块大小占用区直接跳过,这样遇到大块内存占用块可以一下子节约很多for循环的次数,很好理解,但是缺点是如果中间存在多个未利用区域也会浪费for循环的次数。

第二种方式是先计算出需求的内存块大小,然后直接去找对应大小的块,往后推是否为全空。如果不全空,则在那个不空的基础上在向前偏移相应大小,依次计算,这种方法会比第一种更快的找出需求内存块。

举个例子,然后内存池的状态如下,标红的地方为占用,标绿的地方为未被占用。然后需要申请内存块大小为7
在这里插入图片描述

那么如果用原子哥最初的程序,需要for循环 5+6+3+6+2+6+7=35次
如果用屏蔽的算法,需要for循环 5+1+3+1+2+1+7=20次

在这里插入图片描述
如果用第二种方法,需要第一次for到86,然后浪费了两次for循环,然后1次到81,1次到75,一次到69,然后两次回到71,然后到64,最后在数5次,成功申请出来。整个过程需要 1+2+1+1+1+2+1+5=14次。

第二种方法内存块越大,块越碎,越优于第一种。

当然,上述两种方法对连续小块内存分配速度仍然无可奈何。

malloc.h

#ifndef __MALLOC_H
#define __MALLOC_H

#include "api_define_all.h"

#ifndef NULL
#define NULL 0
#endif

#define MEM_BLOCK_SIZE 32								   //内存块大小为32字节
#define MEM_MAX_SIZE 9 * 1024							   //最大管理内存 2K
#define MEM_ALLOC_TABLE_SIZE MEM_MAX_SIZE / MEM_BLOCK_SIZE //内存表大小

#if MEM_BLOCK_SIZE == 32
#define MEM_BLOCK_DIV 5
#elif MEM_BLOCK_SIZE == 16
#define MEM_BLOCK_DIV 4
#elif MEM_BLOCK_SIZE == 8
#define MEM_BLOCK_DIV 3
#elif MEM_BLOCK_SIZE == 4
#define MEM_BLOCK_DIV 2
#elif MEM_BLOCK_SIZE == 2
#define MEM_BLOCK_DIV 1
#endif

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

//内存管理控制器
struct _m_mallco_dev
{
	void (*init)(void);	 //初始化
	u8 (*perused)(void); //内存使用率
	u8 *membase;		 //内存池
	u16 *memmap;		 //内存管理状态表
	u8 memrdy;			 //内存管理是否就绪
};

extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义

void mymemset(void *s, u8 c, u32 count);	//设置内存
void mymemcpy(void *des, void *src, u32 n); //复制内存

void mem_init(void);	  //内存管理初始化函数
u32 mem_malloc(u32 size); //内存分配
u8 mem_free(u32 offset);  //内存释放
u8 mem_perused(void);	  //获得内存使用率

//用户调用函数
void __myfree(void *ptr); //内存释放
#define myfree(x)    \
	{                \
		__myfree(x); \
		x = NULL;    \
	}
void *mymalloc(u32 size);			  //内存分配
void *myrealloc(void *ptr, u32 size); //重新分配内存
#endif

mallc.c

#include "malloc.h"

//内存池(4字节对齐)
/*__align(4)*/ u8 membase[MEM_MAX_SIZE]; //内部SRAM内存池
//内存管理表
u16 memmapbase[MEM_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
//内存管理参数
const u32 memtblsize = MEM_ALLOC_TABLE_SIZE; //内存表大小
const u32 memblksize = MEM_BLOCK_SIZE;       //内存分块大小
const u32 memsize = MEM_MAX_SIZE;            //内存总大小

#if Use_uCOS > 0
OS_SEM MALLOC_SEM;
#endif

//内存管理控制器
struct _m_mallco_dev mallco_dev = {
    mem_init,    //内存初始化
    mem_perused, //内存使用率
    membase,     //内存池
    memmapbase,  //内存管理状态表
    0,           //内存管理未就绪
};

//复制内存
//*des:目的地址
//*src:源地址
//n:需要复制的内存长度(字节为单位)
void mymemcpy(void *des, void *src, u32 n)
{
    u8 *xdes = des;
    u8 *xsrc = src;
    while (n--)
        *xdes++ = *xsrc++;
}

//设置内存
//*s:内存首地址
//c :要设置的值
//count:需要设置的内存大小(字节为单位)
void mymemset(void *s, u8 c, u32 count)
{
    u8 *xs = s;
    while (count--)
        *xs++ = c;
}

//内存管理初始化
//memx:所属内存块
void mem_init(void)
{
    mymemset(mallco_dev.memmap, 0, memtblsize * 2); //内存状态表数据清零
    mymemset(mallco_dev.membase, 0, memsize);       //内存池所有数据清零
    mallco_dev.memrdy = 1;                          //内存管理初始化OK

    OS_ERR err;
    CPU_SR cpu_sr = 0u;
    OS_CRITICAL_ENTER();
    //创建一个信号量
    OSSemCreate((OS_SEM *)&MALLOC_SEM,
                (CPU_CHAR *)"MALLOC_SEM",
                (OS_SEM_CTR)1,
                (OS_ERR *)&err);
    OS_CRITICAL_EXIT();

}

//获取内存使用率
//memx:所属内存块
//返回值:使用率(0~100)
u8 mem_perused(void)
{
    u32 used = 0;
    u32 i;
    for (i = 0; i < memtblsize; i++)
    {
        if (mallco_dev.memmap[i])
            used++;
    }
    return (used * 100) / (memtblsize);
}

//内存分配(内部调用)
//memx:所属内存块
//size:要分配的内存大小(字节)
//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
u32 mem_malloc(u32 size)
{
    signed long offset = 0;
    u16 nmemb = 0; //需要的内存块数
    u16 cmemb = 0; //连续空内存块数
    u32 i = 0;
    if (!mallco_dev.memrdy)
        mallco_dev.init(); //未初始化,先执行初始化
    if (size == 0)
        return 0XFFFFFFFF; //不需要分配
        
    OS_ERR err;
    OSSemPend(&MALLOC_SEM, 0, OS_OPT_PEND_BLOCKING, 0, &err); //请求信号量
    nmemb = size >> MEM_BLOCK_DIV;                            //获取需要分配的连续内存块数
    if (size % memblksize)
        nmemb++;

    // for (offset = memtblsize - 1; offset >= 0; offset--) //搜索整个内存控制区
    // {
    //     if (!mallco_dev.memmap[offset])
    //         cmemb++; //连续空内存块数增加
    //     else
    //     {
    //         cmemb = 0; //连续内存块清零
    //         if (offset >= mallco_dev.memmap[offset])
    //             offset -= (mallco_dev.memmap[offset] - 1); //当内存块占用的大小
    //         else
    //             break; /*如果不够减,说明内存池已经不够用了*/
    //     }
    //     if (cmemb == nmemb) //找到了连续nmemb个空内存块
    //     {
    //         for (i = 0; i < nmemb; i++) //标注内存块非空
    //         {
    //             mallco_dev.memmap[offset + i] = nmemb;
    //         }
    //         OSSemPost(&MALLOC_SEM, OS_OPT_POST_1, &err); //发送信号量
    //         return (offset * memblksize);                //返回偏移地址
    //     }
    // }

    if (memtblsize > nmemb)
    {
        offset = memtblsize - nmemb;
        for (i = 0; i + cmemb < nmemb;)
        {
            if (mallco_dev.memmap[offset + i]) //内存块不空
            {
                if (offset + i >= nmemb) //即offset + i一定大于等于一
                {
                    if (mallco_dev.memmap[offset + i - 1] == mallco_dev.memmap[offset + i]) //说明落点在连续已被占有内存块内
                    {
                        if (offset + i > mallco_dev.memmap[offset + i])
                            offset = offset + i - mallco_dev.memmap[offset + i];
                        else
                            break;
                    }
                    else
                    {
                        offset = offset + i - nmemb;
                    }
                    cmemb = i;
                    i = 0;
                }
                else
                    break; //剩余大小已经不够分配了
            }
            else
                i++;
        }
        cmemb += i;
        if (cmemb == nmemb)
        {
            for (i = 0; i < nmemb; i++) //标注内存块非空
            {
                mallco_dev.memmap[offset + i] = nmemb;
            }
            OSSemPost(&MALLOC_SEM, OS_OPT_POST_1, &err); //发送信号量
            return (offset * memblksize);                //返回偏移地址
        }
    }

    OSSemPost(&MALLOC_SEM, OS_OPT_POST_NONE, &err); //发送信号量
    return 0XFFFFFFFF; //未找到符合分配条件的内存块
}

//释放内存(内部调用)
//memx:所属内存块
//offset:内存地址偏移
//返回值:0,释放成功;1,释放失败;
u8 mem_free(u32 offset)
{
    int i;
    if (!mallco_dev.memrdy) //未初始化,先执行初始化
    {
        mallco_dev.init();
        return 1; //未初始化
    }
    if (offset < memsize) //偏移在内存池内.
    {
        int index = offset / memblksize;      //偏移所在内存块号码
        int nmemb = mallco_dev.memmap[index]; //内存块数量
        for (i = 0; i < nmemb; i++)           //内存块清零
        {
            mallco_dev.memmap[index + i] = 0;
        }
        return 0;
    }
    else
        return 2; //偏移超区了.
}

//释放内存(外部调用)
//memx:所属内存块
//ptr:内存首地址
void __myfree(void *ptr)
{
    u32 offset;
    if (ptr == NULL)
        return; //地址为0.
    offset = (u32)ptr - (u32)mallco_dev.membase;
    mem_free(offset); //释放内存
}

//分配内存(外部调用)
//memx:所属内存块
//size:内存大小(字节)
//返回值:分配到的内存首地址.
void *mymalloc(u32 size)
{
    u32 offset = mem_malloc(size);
    if (offset == 0XFFFFFFFF)
        return NULL;
    else
        return (void *)((u32)mallco_dev.membase + offset);
}

//重新分配内存(外部调用)
//memx:所属内存块
//*ptr:旧内存首地址
//size:要分配的内存大小(字节)
//返回值:新分配到的内存首地址.
void *myrealloc(void *ptr, u32 size)
{
    u32 offset;
    offset = mem_malloc(size);
    if (offset == 0XFFFFFFFF)
        return NULL;
    else
    {
        mymemcpy((void *)((u32)mallco_dev.membase + offset), ptr, size); //拷贝旧内存内容到新内存
        myfree(ptr);                                                     //释放旧内存
        return (void *)((u32)mallco_dev.membase + offset);               //返回新内存首地址
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜暝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值