查看: 1296|回复: 0

基于Buddy算法的动态内存内存管理方案(GD32 小红板实现)

[复制链接]
  • TA的每日心情
    开心
    2015-7-14 10:15
  • 签到天数: 4 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2016-1-5 08:56:13 | 显示全部楼层 |阅读模式
    分享到:
    这个算法是我在最新版的trochili rtos中实现的,单独拿出来用也挺有意义的。如果有些同学想要移植到自己的系统中,注意重新定义一下数据类型。
    不多说,上源码,看不懂的同学得先去看看buddy算法的核心二叉树的实现。这个buddy算法还是非常好用的。在这个实现里我顺便添加了bit位图来表示内存页是否被分配出去,防止二次释放的危险(某些著名的rtos可没这么友好)。

    /* 动态内存管理配置 */
    #define TCL_MEMORY_ENABLE         (1)
    #define TCL_MEMORY_POOL_ENABLE       (1)
    #define TCL_MEMORY_POOL_PAGES        (256U)
    #define TCL_MEMORY_BUDDY_ENABLE     (1)
    #define TCL_MEMORY_BUDDY_PAGES       (64)

    /*****************************************************************
    *                          Trochili RTOS Kernel                         *
    *                        Copyright(C) 2015 LIUXUMING                        *
    *                           www.trochili.com                            *
    *****************************************************************/
    #ifndef _TCL_MEMORY_BUDDY_H
    #define _TCL_MEMORY_BUDDY_H

    #include "tcl.types.h"
    #include "tcl.config.h"
    #include "tcl.memory.h"

    #if ((TCL_MEMORY_ENABLE) && (TCL_MEMORY_BUDDY_ENABLE))

    #define MEM_BUDDY_PAGE_TAGS ((TCL_MEMORY_BUDDY_PAGES + 31u) >> 5u)
    #define MEM_BUDDY_NODE_TAGS (TCL_MEMORY_BUDDY_PAGES * 2u - 1u)
    #define MEM_PROP_READY (1)

    typedef struct MemBuddyDef
    {
        TProperty Property;               /* 内存页池属性                */
        TChar*    PageAddr;               /* 被管理的内存的起始地址         */
        TWord   PageSize;               /* 内存页大小                 */
        TWord   PageNbr;                 /* 内存页数目                 */
        TWord   PageAvail;                /* 可用内存页数目             */
        TBitMask PageTags[MEM_BUDDY_PAGE_TAGS]; /* 内存页是否可用标记            */
        TWord   NodeNbr;
        TByte   NodeTags[MEM_BUDDY_NODE_TAGS];
    } TMemBuddy;

    extern TState xBuddyInit(TMemBuddy* pBuddy, TChar* pAddr, TWord pages, TWord pagesize, TError* pError);
    extern TState xBuddyDeinit(TMemBuddy* pBuddy, TError* pError);
    extern TState xBuddyMemMalloc(TMemBuddy* pBuddy, TWord length, void** pAddr, TError* pError);
    extern TState xBuddyMemFree(TMemBuddy* pBuddy, void* pAddr, TError* pError);

    #endif

    #endif /* _TCL_MEMORY_BUDDY_H */

    /*****************************************************************
    *                          Trochili RTOS Kernel                         *
    *                        Copyright(C) 2015 LIUXUMING                        *
    *                           www.trochili.com                            *
    *****************************************************************/
    #include <string.h>

    #include "tcl.config.h"
    #include "tcl.types.h"
    #include "tcl.cpu.h"
    #include "tcl.debug.h"
    #include "tcl.mem.buddy.h"

    #if ((TCL_MEMORY_ENABLE) && (TCL_MEMORY_BUDDY_ENABLE))

    /* BUDDY属性标记定义 */
    #define BUDDY_PROP_NONE       (0x0)             /* BUDDY空属性标记               */
    #define BUDDY_PROP_READY        (0x1<<0u)         /* BUDDY就绪标记               */

    #define PAGES_AVAIL (0x1<<7u)

    #define PARENT_NODE(x) (((x) - 1u) / 2u)
    #define LEFT_NODE(x) ((x) * 2u + 1u)
    #define RIGHT_NODE(x) ((x) * 2u + 2u)

    /* 调整x到和它最相近且不小于它的2的幂数 */
    static TWord clp2(TWord x)
    {
        x = x - 1U;
        x = x | (x >> 1U);
        x = x | (x >> 2U);
        x = x | (x >> 4U);
        x = x | (x >> 8U);
        x = x | (x >> 16U);
        return (x + 1U);
    }

    /* 调整x到和它最相近且不大于它的2的幂数 */
    static TWord flp2(TWord x)
    {
        x = x | (x >> 1U);
        x = x | (x >> 2U);
        x = x | (x >> 4U);
        x = x | (x >> 8U);
        x = x | (x >> 16U);
        return (x - (x >> 1U));
    }

    /* 计算x以2为底的对数 */
    static TWord log2(TWord x)
    {
        TWord i = 0;
        while (!(x &0x1))
        {
         i++;
         x = x >> 1;
        }

        return i;
    }

    /* 计算2的x次幂 */
    static TWord power2(TWord x)
    {
        TWord i = 1;
        while (x)
        {
         i = i << 1;
         x--;
        }

        return i;
    }


    /*****************************************************************
    * 功能:分配每个二叉树节点管理的内存页数                                      *
    * 参数:(1) pBuddy 伙伴系统分配器地址                                       *
    * 返回: 无                                                          *
    * 说明:                                                           *
    *****************************************************************/
    static void BuildPageTree(TMemBuddy* pBuddy)
    {
        TIndex node;
        U32 logn;
        U32 x;
        U32 y;

        /* 计算树的节点总数 */
        pBuddy->NodeNbr = pBuddy-&gtageNbr * 2u - 1u;

        /* 计算每个节点管理的页数(采用以2为底的对数来表示) */
        logn = log2(pBuddy-&gtageNbr) & 0x3f;
        node = 0u;
        for (y = 0; y <= logn; y++)
        {
         x = (pBuddy-&gtageNbr >> (logn - y));
         while (x--)
         {
             pBuddy->NodeTags[node] = ((logn - y) | PAGES_AVAIL);
             node++;
         }
        }
    }


    static int GetAvailPages(TMemBuddy* pBuddy)
    {
        TByte tag;
        tag = pBuddy->NodeTags[0];
        if (tag & PAGES_AVAIL)
        {
         return power2(tag & 0x3f);
        }
        return 0;
    }

    /*****************************************************************
    * 功能:从伙伴管理器中分配一定数目的页                                       *
    * 参数:(1) pBuddy    伙伴系统分配器对象地址                                   *
    *     (2) pages   需要分配的内存页数                                      *
    *     (3) index   分配得到的内存页号                                      *
    * 返回: (1) 分配到的页面的编号                                           *
    * 说明:                                                           *
    *****************************************************************/
    static TWord MallocPages(TMemBuddy* pBuddy, TWord pages)
    {
        TWord index;
        TWord lvl;
        TWord node;
        TByte tag;
        TByte logn;

        TByte ltag;
        TByte rtag;
        TByte llogn;
        TByte rlogn;

        /* 计算和pages对应的以2为底的对数 */
        logn = log2(pages);

        /* 计算需要遍历的(从root算起)层数 */
        lvl = log2(pBuddy-&gtageNbr) - logn;

        /* 从根节点开始遍历lvl次,找到可供分配的节点 */
        node = 0u;
        while (lvl--)
        {
         tag = (pBuddy->NodeTags[LEFT_NODE(node)]);
         if ((tag & PAGES_AVAIL) && ((tag & 0x3f) >= logn))
         {
             node = LEFT_NODE(node);
         }
         else
         {
             node = RIGHT_NODE(node);
         }
        }

        /* 取消分配给该节点的内存页 */
        pBuddy->NodeTags[node] = 0u;

        /* 通过节点编号计算内存页编号 */
        index = (node + 1u) * pages - pBuddy-&gtageNbr;

        /* 回溯调整到根节点路径上的所有节点的可分配内存页数(注意是路径上的全部节点) */
        while (node)
        {
         node = PARENT_NODE(node);
         ltag = pBuddy->NodeTags[LEFT_NODE(node)];
         rtag = pBuddy->NodeTags[RIGHT_NODE(node)];
         if ((ltag & PAGES_AVAIL) && (rtag & PAGES_AVAIL))
         {
             llogn = (ltag & 0x3f);
             rlogn = (rtag & 0x3f);
             tag   = (llogn > rlogn) ? llogn : rlogn;
             tag   |= PAGES_AVAIL;
         }
         else if (ltag & PAGES_AVAIL)
         {
             tag = ltag;
         }
         else if (rtag & PAGES_AVAIL)
         {
             tag = rtag;
         }
         else
         {
             tag = 0u;
         }

         pBuddy->NodeTags[node] = tag;
        }

        return index;
    }


    /*****************************************************************
    * 功能:线程通用阻塞终止函数,将指定的线程从阻塞队列中终止阻塞                        *
    * 参数:(1) pBuddy    伙伴系统分配器对象地址                                   *
    *     (2) index   待释放的多个内存页的起始页号                               *
    * 返回: 无                                                          *
    * 说明:                                                           *
    *****************************************************************/
    static TWord FreePages(TMemBuddy* pBuddy, TWord index)
    {
        TWord pages;
        TWord node;
        TByte tag;
        TByte logn;
        TWord lvl;

        TByte ltag;
        TByte rtag;
        TByte llogn;
        TByte rlogn;

        /* 计算根节点以2为底的对数 */
        lvl = log2(pBuddy-&gtageNbr);

        /* 通过内存页编号获得叶子节点编号 */
        node = index + pBuddy-&gtageNbr - 1u;

        /* 通过该叶子节点向上回溯查找分配该起始内存页的节点, 比对次数最多为树的深度 */
        for (logn = 0; logn <= lvl; logn++)
        {
         /* 如果找到分配该内存(n)页的那个节点 */
         if (!(pBuddy->NodeTags[node] & PAGES_AVAIL))
         {
             break;
         }
         node = PARENT_NODE(node);
        }

        /* 回收该相关内存(n)页,重新计算它可以管理的内存页数 */
        pBuddy->NodeTags[node] = ((logn & 0x3f) | PAGES_AVAIL);
        pages = power2(logn);

        /* 尝试进行进行伙伴节点合并,需要一直遍历到root节点。
          如果是根节点则不需要以下操作 */
        while (node)
        {
         node = PARENT_NODE(node);
         logn++;
         ltag = pBuddy->NodeTags[LEFT_NODE(node)];
         rtag = pBuddy->NodeTags[RIGHT_NODE(node)];

         if ((ltag &ampAGES_AVAIL) && (rtag &ampAGES_AVAIL))
         {
             llogn = (ltag &0x3f);
             rlogn = (rtag &0x3f);
             if (power2(llogn) + power2(rlogn) == power2(logn))
             {
                tag = ((logn &0x3f) | PAGES_AVAIL);
             }
             else
             {
                tag = (llogn > rlogn) ? llogn : rlogn;
                tag |= PAGES_AVAIL;
             }
         }
         else if (ltag &ampAGES_AVAIL)
         {
             tag = ltag;
         }
         else //if (rtag &PAGES_AVAIL)
         {
             tag = rtag;
         }
         pBuddy->NodeTags[node] = tag;
        }

        return pages;
    }


    /*****************************************************************
    * 功能:初始化伙伴内存管理控制结构                                          *
    * 参数:(1) pBuddy    伙伴系统分配器内存地址                                   *
    *     (2) pAddr   可供分配的内存地址                                      *
    *     (3) pagesize 内存页大小                                           *
    *     (4) pages   可供分配的内存页总数                                   *
    *     (5) pError    详细调用结果                                          *
    * 返回: (1) eSuccess 操作成功                                           *
    *     (2) eFailure 操作失败                                           *
    * 说明:                                                           *
    *****************************************************************/
    TState xBuddyInit(TMemBuddy* pBuddy, TChar* pAddr, TWord pages, TWord pagesize, TError* pError)
    {
        TState state = eFailure;
        TError error = MEM_ERR_FAULT;
        TIndex index;

        TReg32 imask;

        CpuEnterCritical(&imask);
        if (!(pBuddy->Property & BUDDY_PROP_READY))
        {
         /* 调整pages到和它最相近且不大于它的2的幂数 */
         pages = flp2(pages);
         if (pages)
         {
             pBuddy->Property = BUDDY_PROP_READY;
             pBuddy->PageAddr = pAddr;
             pBuddy->PageSize = pagesize;
             pBuddy->PageNbr   = pages;
             pBuddy->PageAvail = pages;

             /* 设置所有内存都处于可分配状态 */
             for (index = 0; index < MEM_BUDDY_PAGE_TAGS; index++)
             {
                pBuddy->PageTags[index] = ~0U;
             }

             /* 创建二叉树控制结构 */
             BuildPageTree(pBuddy);

             error = MEM_ERR_NONE;
             state = eSuccess;
         }
        }
        CpuLeaveCritical(imask);

        *pError = error;
        return state;
    }


    /*****************************************************************
    * 功能:销毁伙伴内存管理控制结构                                           *
    * 参数:(1) pBuddy   伙伴系统分配器内存地址                                 *
    *     (2) pError   详细调用结果                                         *
    * 返回: (1) eSuccess   操作成功                                            *
    *     (2) eFailure   操作失败                                            *
    * 说明:                                                           *
    *****************************************************************/
    TState xBuddyDeinit(TMemBuddy* pBuddy, TError* pError)
    {
        TReg32 imask;
        TState state = eFailure;
        TError error = MEM_ERR_UNREADY;

        CpuEnterCritical(&imask);
        if (pBuddy->Property & MEM_PROP_READY)
        {
         memset(pBuddy->PageAddr, 0u, pBuddy->PageSize * pBuddy->PageNbr);
         memset(pBuddy, 0u, sizeof(TMemBuddy));
         error = MEM_ERR_NONE;
         state = eSuccess;
        }
        CpuLeaveCritical(imask);

        *pError = error;
        return state;
    }


    /*****************************************************************
    * 功能:从伙伴系统中申请内存                                              *
    * 参数:(1) pBuddy    伙伴系统分配器对象地址                                   *
    *     (2) len      需要分配的内存长度                                      *
    *     (3) pAddr2    分配得到的内存地址指针                                   *
    *     (4) pError    详细调用结果                                          *
    * 返回: (1) eSuccess 操作成功                                           *
    *     (2) eFailure 操作失败                                           *
    * 说明:                                                           *
    *****************************************************************/
    TState xBuddyMemMalloc(TMemBuddy* pBuddy, TWord length, void** pAddr2, TError* pError)
    {
        TState state = eFailure;
        TError error = MEM_ERR_UNREADY;
        TReg32 imask;
        TWord pages;
        TWord index;
        TWord avail;
        TIndex x;
        TIndex y;
        TIndex i;

        CpuEnterCritical(&imask);

        if (pBuddy->Property &BUDDY_PROP_READY)
        {
         /* 如果申请的内存长度没有超过BUDDY的范围 */
         if (length <= (pBuddy->PageNbr * pBuddy->PageSize))
         {
             /* 计算需要分配多少内存页 */
             pages = (length + pBuddy->PageSize - 1u) / (pBuddy->PageSize);

             /* 调整pages到和它最相近且不小于它的2的幂数 */
             pages = clp2(pages);

             avail = GetAvailPages(pBuddy);
             if (avail >= pages)
             {
                /* 获得分配的内存页编号 */
                index = MallocPages(pBuddy, pages);

                /* 标记该部分内存页已经被分配 */
                for (i = 0; i < pages; i++)
                {
                 y = ((index + i) >> 5);
                 x = ((index + i) & 0x1f);
                 pBuddy->PageTags[y] &= ~(0x1 << x);
                }
                pBuddy->PageAvail -= pages;

                /* 通过内存页编号获得内存地址 */
                *pAddr2 = (void*)(pBuddy->PageAddr + index * pBuddy->PageSize);

                error = MEM_ERR_NONE;
                state = eSuccess;
             }
             else
             {
                *pAddr2 = (void*)0;
                error = MEM_ERR_NO_MEM;
             }
         }
         else
         {
             error = MEM_ERR_NO_MEM;
         }
        }
        CpuLeaveCritical(imask);

        *pError = error;
        return state;
    }


    /*****************************************************************
    * 功能:向伙伴系统中释放内存                                              *
    * 参数:(1) pBuddy    伙伴系统分配器对象地址                                   *
    *     (2) pAddr   待释放的内存地址                                       *
    *     (3) pError    详细调用结果                                          *
    * 返回: (1) eSuccess 操作成功                                           *
    *     (2) eFailure 操作失败                                           *
    * 说明:                                                           *
    *****************************************************************/
    TState xBuddyMemFree(TMemBuddy* pBuddy, void* pAddr, TError* pError)
    {
        TState state = eFailure;
        TError error = MEM_ERR_UNREADY;
        TReg32 imask;
        TWord index;
        TIndex x;
        TIndex y;
        TBitMask tag;
        TWord pages;
        TIndex i;

        CpuEnterCritical(&imask);
        if ((pBuddy->Property &BUDDY_PROP_READY))
        {
         /* 检查被释放的地址是否在伙伴系统管理的内存范围内 */
         if (((char*)pAddr >= (char*)(pBuddy->PageAddr)) &&
             ((char*)pAddr < ((char*)(pBuddy->PageAddr) + pBuddy->PageSize* pBuddy->PageNbr)))
         {
             /* 通过内存地址计算起始页编号 */
             index = ((char*)pAddr - (char*)(pBuddy->PageAddr)) / pBuddy->PageSize;

             /* 检查内存页管理标记,避免再次释放已经释放过的内存页地址 */
             y = (index >> 5);
             x = (index & 0x1f);
             tag = pBuddy->PageTags[y] & (0x1 << x);
             if (tag == 0)
             {
                /* 释放该页起始的内存 */
                pages = FreePages(pBuddy, index);

                /* 标记该部分内存可以被分配 */
                for (i = 0; i < pages; i++)
                {
                 y = (index + i) >> 5;
                 x = (index + i) & 0x1f;
                 pBuddy->PageTags[y] |= (0x1 << x);
                }

                pBuddy->PageAvail += pages;
                error = MEM_ERR_NONE;
                state = eSuccess;
             }
             else
             {
                error = MEM_ERR_DBL_FREE;
             }
         }
         else
         {
             error = MEM_ERR_BAD_ADDR;
         }
        }
        CpuLeaveCritical(imask);

        *pError = error;
        return state;
    }

    #endif
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /5 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-12-19 20:36 , Processed in 0.130777 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.