tars源码漫谈第46篇------tc_malloc_chunk.h/tc_malloc_chunk.cpp(内存块操作)

      看了一下, 在tars源码中, 并没有用到tc_malloc_chunk中的类, 所以也不打算详细介绍, 了解一下即可。 cpp文件就不看了, 看下头文件:

#ifndef __TC_MALLOC_CHUNK_H
#define __TC_MALLOC_CHUNK_H

#include <iostream>
#include <cassert>
#include <stdlib.h>
#include <string.h>
#include <vector>

using namespace std;

namespace tars
{
    /
    /**  
    * @file tc_mem_chunk.h
    * @brief 内存分配器 
    *          
    */ 
    static const size_t kPageShift  = 15;
    static const size_t kNumClasses = 78;
    static const size_t kPageSize   = 1 << kPageShift;
    static const size_t kMaxSize    = 256 * 1024;
    static const size_t kAlignment  = 8;
    static const size_t kMaxPages    = 1 << (20 - kPageShift);
    static const size_t kPageAlignment = 64;

    
    /**
    * 请求的内存大小与请求实际得到的内存大小的映射
    * 实际得到的内存大小与内存大小所属的大小类别的映射
    * 内存大小所属的大小类别与该内存大小类别需要的内存页的映射
    */
    class SizeMap
    {
    public:
        SizeMap() { Init(); }    

        /*
        *size所属的尺寸类别
        */
        inline int SizeClass(int size)
        {
            return class_array_[ClassIndex(size)];
        }

        /*
        *尺寸类别cl对应的尺寸大小
        */
        inline size_t ByteSizeForClass(size_t cl)
        {
            return class_to_size_[cl];
        }

        /*
        *尺寸类别cl对应的页数
        */
        inline size_t class_to_pages(size_t cl)
        {
            return class_to_pages_[cl];
        }

    private:
        static inline size_t ClassIndex(int s)
        {
            const bool big = (s > kMaxSmallSize);
            const size_t  add_amount= big ? (127 + (120<<7)) : 7;
            const size_t  shift_amount = big ? 7 : 3;
            return (s + add_amount) >> shift_amount;
        }

        static inline int LgFloor(size_t n)
        {
            int log = 0;
            for (int i = 4; i >= 0; --i)
            {
                int shift = (1 << i);
                size_t x = n >> shift;
                if (x != 0)
                {
                    n = x;
                    log += shift;
                }
            }

            return log;
        }

        /*
        *初始化
        */
        void Init();

        size_t AlignmentForSize(size_t size);

        int NumMoveSize(size_t size);
    private:
        static const int kMaxSmallSize        = 1024;
        static const size_t kClassArraySize = ((kMaxSize + 127 + (120 << 7)) >> 7) + 1;
        size_t            class_to_size_[kNumClasses];
        size_t            class_to_pages_[kNumClasses];
        unsigned char    class_array_[kClassArraySize];

    };
    
    class Static
    {
    public:
        static SizeMap* sizemap()
        {
            return &_sizemap;
        }
    private:
        static SizeMap _sizemap;
    };

    
    /**
    * TC_Span内存分配器
    *
    * 用于分配sizeof(TC_Span)大小的内存块
    */
    class TC_SpanAllocator
    {
    public:
        TC_SpanAllocator() : _pHead(NULL), _pData(NULL) {}

        void* getHead() const { return _pHead; }

        /**
         * 初始化
         * @param pAddr, 地址, 换到应用程序的绝对地址
         * @param mem_size, 内存大小
         */
        void create(void* pAddr, size_t iSpanSize, size_t iSpanCount);

        /**
         * 连接上
         * @param pAddr, 地址, 换到应用程序的绝对地址
         */
        void connect(void* pAddr);

        /**
         * 重建
         */
        void rebuild();

        /**
         * 是否还有可用block
         *
         * @return bool
         */
        bool isSpanAvailable() const { return _pHead->_spanAvailable > 0; }

        /**
         * 分配一个区块
         *
         * @return void*
         */
        void* allocate();

        /**
         * 释放区块
         * @param pAddr
         */
        void deallocate(void *pAddr);

        struct tagSpanAlloc
        {
            size_t  _iSpanSize;            /**span区块大小*/
            size_t  _iSpanCount;           /**span个数*/
            size_t  _firstAvailableSpan;   /**第一个可用的span索引*/
            size_t  _spanAvailable;        /**可用span个数*/
        }__attribute__((packed));

        static size_t getHeadSize() { return sizeof(tagSpanAlloc); }

    protected:
        /**
         * 初始化
         */
        void init(void *pAddr);

        //禁止copy构造
        TC_SpanAllocator(const TC_SpanAllocator &mcm);

        //禁止复制
        TC_SpanAllocator &operator=(const TC_SpanAllocator &mcm);

    private:
        /**
         * 区块头指针
         */
        tagSpanAlloc    *_pHead;

        /**
         * 数据区指针
         */
        unsigned char   *_pData;
    };

    
    /**
    * 内存分配器
    *
    * 将连续的内存按32K大小分页
    * 并通过TC_Span和PageMap进行管理
    */
    class TC_Page
    {
    public:
        struct tagShmFlag
        {
            bool         _bShmProtectedArea;    /*是否设置保护区*/
            int          _iShmFlag;                /*内存空间标识,内存未使用前为0,使用后为1*/
            size_t       _iShmMemSize;            /*内存空间大小*/
            size_t       _iShmSpanAddr;            /*TC_Span内存区的起始地址,相对地址*/
            size_t       _iShmPageMapAddr;        /*PageMap内存区的起始地址,相对地址*/
            size_t       _iShmPageAddr;            /*Data区的内存区的其实地址,相对地址*/
            size_t       _iShmSpanNum;            /*TC_Span内存区中span的个数*/
            size_t       _iShmPageMapNum;        /*PageMap内存区中map的个数*/
            size_t       _iShmPageNum;            /*Data内存区中页的个数*/
        }__attribute__((packed));

        struct tagModifyData
        {
            size_t       _iModifyAddr;            /*修改的地址*/
            char         _cBytes;                /*字节数*/
            size_t       _iModifyValue;            /*值*/
        }__attribute__((packed));

        struct tagModifyHead
        {
            char           _cModifyStatus;        /*修改状态: 0:目前没有人修改, 1: 开始准备修改, 2:修改完毕, 没有copy到内存中*/
            size_t         _iNowIndex;            /*更新到目前的索引*/
            tagModifyData  _stModifyData[32];    /*一次最多32次修改*/
        }__attribute__((packed));

        struct TC_Span
        {
            size_t       start;                    /*span所管理的页内存的起始页号*/
            size_t       length;                /*页的个数*/
            size_t       next;
            size_t       prev;
            size_t       objects;                /*用于分配内存*/
            unsigned int refcount;                /*按照sizeclass类大小划分span拥有的内存后的个数*/
            unsigned int sizeclass;                /*内存大小的类别*/
            unsigned int location;                /*用于识别该span是空闲,还是处于使用中*/

            enum { IN_USE, ON_FREELIST };
        }__attribute__((packed));    

        struct TC_CenterList
        {
            size_t       size_class;            /*内存大小的类别*/
            TC_Span      empty;                    /*空闲链表*/
            TC_Span      nonempty;                /*非空闲链表*/
        }__attribute__((packed));

    public:
        TC_Page() : _pShmFlagHead(NULL),_pModifyHead(NULL),_pCenterCache(NULL),_pLarge(NULL),_pFree(NULL),_pSpanMemHead(NULL),_pPageMap(NULL),_pData(NULL) {}

        /**
         * 初始化
         * @param pAddr, 地址, 换到应用程序的绝对地址
         * @param iShmMemSize, 内存大小
         * @param bProtectedArea, 是否使用保护区
         */
        void create(void *pAddr, size_t iShmMemSize, bool bProtectedArea);

        /**
         * 连接上
         * @param pAddr, 地址, 换到应用程序的绝对地址
         */
        void connect(void *pAddr);

        /**
         * 重建
         */
        void rebuild();

        /**
         * 分配一个区块
         * @param iClassSize,需要分配的内存大小类别
         * @param iAllocSize, 分配的数据块大小
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         * @return void *
         */
        void* fetchFromSpansSafe(size_t iClassSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);

        /**
         * 释放内存, 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         */
        void  releaseToSpans(size_t iPageId, size_t iIndex);

        /**
         * 释放内存, 绝对地址
         * @param pAddr
         */
        void  releaseToSpans(void* pObject);

        /**
         * 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个得到该内存块的起始绝对地址
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         */
        void* getAbsolute(size_t iPageId, size_t iIndex);

        /**
         * 修改更新到内存中
         */
        void doUpdate(bool bUpdate = false);

        /**
         * 获得用于存放数据的页内存的数量
         */
        inline size_t getPageNumber();

        /**
         * 获得用于存放数据的页内存的大小
         */
        inline size_t getPageMemSize();

        /**
         * 获得TC_Page所管理内存的结束位置
         */
        inline size_t getPageMemEnd();

        /**
         * 传给TC_Page的内存的最小大小
         */
        static size_t getMinMemSize() 
        { 
            return sizeof(tagShmFlag) + sizeof(tagModifyHead) + sizeof(TC_CenterList) * kNumClasses + sizeof(TC_Span) + sizeof(TC_Span) * kMaxPages + TC_SpanAllocator::getHeadSize() + sizeof(TC_Span) + sizeof(size_t) + kPageSize; 
        }

    protected:

        //禁止copy构造
        TC_Page(const TC_Page &mcm);

        //禁止复制
        TC_Page &operator=(const TC_Page &mcm);

        /**
         * 初始化
         */
        void init(void *pAddr);

        /**
         * 按页初始化用于存放数据的内存
         */
        void initPage(void *pAddr);


        /
        /**
         * 初始化list双向链表
         */
        inline void DLL_Init(TC_Span* list, size_t iIndex);

        /**
         * 从双向链表中删除span指向的节点
         */
        inline void DLL_Remove(TC_Span* span);

        /**
         * 将span指向的节点加入到双向链表list中
         */
        inline void DLL_Prepend(TC_Span* list, TC_Span* span);

        /**
         * 双向链表list是否为空
         */
        inline bool DLL_IsEmpty(TC_Span* list, size_t iIndex) 
        {
          return list->next == iIndex;
        }

        //
        /**
         * 得到PageMap中第k个元素存放的指针值,其实际意义就是看Data内存区中第k块内存页属于哪个TC_Span管理
         */
        size_t Get(size_t k) const
        {
            return _pPageMap[k];
        }

        /**
         * 设置PageMap中第k个元素存放的指针值,其实际意义就是看Data内存区中第k块内存页属于哪个TC_Span管理
         */
        void Set(size_t k, size_t v)
        {
            _pPageMap[k] = v;
        }

        /**
         * 确保n未超过页内存的数目与k之差
         */
        bool Ensure(size_t k, size_t n)
        {
            return n <= _pShmFlagHead->_iShmPageMapNum -k;
        }
        //
        /**
         * create或rebuild的时候会调用,用于初始化时将所有的内存页映射到_pLarge上
         */
        bool UseShmMem();

        /**
         * 得到内存页pPageId属于哪个TC_Span管理
         */
        TC_Span* GetDescriptor(size_t pPageId);

        /**
         * 在_pFree或_pLarge链表中查找n块连续的内存页,通过TC_Span返回
         */
        TC_Span* SearchFreeAndLargeLists(size_t n);

        /**
         * 分配n块连续的内存页,通过TC_Span返回,里面会调用SearchFreeAndLargeLists函数
         */
        TC_Span* New(size_t n);

        /**
         * 在_pLarge链表中分配n块连续的内存页,通过TC_Span返回
         */
        TC_Span* AllocLarge(size_t n);

        /**
         * 在span所管理的内存页中,分配出n块连续内存
         */
        TC_Span* Carve(TC_Span* span, size_t n);

        /**
         * 根据span所管理的内存页的数目,将该span插入到_pLarge或_pFree链表中
         */
        void PrependToFreeList(TC_Span* span);

        /**
         * 将span从它所属的链表中删除,里面调用MergeIntoFreeList
         */
        void Delete(TC_Span* span);

        /**
         * 将span从它所属的链表中删除,并且查看该span所管理的内存页前后连续的内存页是否空闲,若是,则进行合并操作
         */
        void MergeIntoFreeList(TC_Span* span);

        /**
         * 设置span所管理的内存页将要进行分割的内存大小类别
         */
        void RegisterSizeClass(TC_Span* span, size_t sc);

        /**
         * 将span所属的内存页,映射到_pPageMap中
         */
        void RecordSpan(TC_Span* span);

        //

        /**
         * 分配一个区块
         * @param iClassSize,需要分配的内存大小类别
         * @param iAllocSize, 分配的数据块大小
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         * @return void *
         */
        void* FetchFromSpans(size_t iClassSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);

        /**
         * 按iClassSize类别的内存大小分割内存页
         */
        int Populate(size_t iClassSize);

        /**
         * 修改具体的值
         * @param iModifyAddr
         * @param iModifyValue
         */
        //inline void update(void* iModifyAddr, size_t iModifyValue);
        template<typename T>
        void update(void* iModifyAddr, T iModifyValue)
        {
            _pModifyHead->_cModifyStatus = 1;
            _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._iModifyAddr  = reinterpret_cast<size_t>(iModifyAddr) - reinterpret_cast<size_t>(_pShmFlagHead);
            _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._iModifyValue = iModifyValue;
            _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._cBytes       = sizeof(iModifyValue);
            _pModifyHead->_iNowIndex++;

            assert(_pModifyHead->_iNowIndex < sizeof(_pModifyHead->_stModifyData) / sizeof(tagModifyData));
        }
    
    private:
        /**
         * 头部内存块头指针
         */
        tagShmFlag                *_pShmFlagHead;
        /**
         * 保护区内存块头指针
         */
        tagModifyHead             *_pModifyHead;
        /**
         * 中央自由链表头指针
         */
        TC_CenterList             *_pCenterCache;
        /**
         * 大块内存页的链表指针(大于等于1MB内存块的链表)
         */
        TC_Span                   *_pLarge;
        /**
         * 小块内存页的链表头指针(小于1MB的内存块)
         */
        TC_Span                   *_pFree;
        /**
         * 用于分配TC_Span的内存区域的头指针
         */
        TC_SpanAllocator          *_pSpanMemHead;
        /**
         * 与数据区内存相映射的内存区头指针
         */
        //void                      **_pPageMap;
        size_t                      *_pPageMap;
        /**
         * 数据区的头指针
         */
        void                      *_pData;

        /**
         * 用于分配TC_Span的内存分配器
         */
        TC_SpanAllocator          _spanAlloc;
    };

    
    class TC_MallocChunkAllocator
    {
    public:
        TC_MallocChunkAllocator():_pHead(NULL),_pChunk(NULL),_nallocator(NULL) {}

        ~TC_MallocChunkAllocator()
        {
            clear();
        }

        void clear()
        {
            if(_nallocator)
            {
                delete _nallocator;
                _nallocator = NULL;
            }

            _pHead = NULL;
            _pChunk = NULL;
        }

        /**
         * 初始化
         * @param pAddr, 地址, 换到应用程序的绝对地址
         * @param iSize, 内存大小
         * @param bProtectedArea, 是否使用保护区,默认使用
         */
        void create(void *pAddr, size_t iSize, bool bProtectedArea = true);

        /**
         * 连接上
         * @param pAddr, 地址, 换到应用程序的绝对地址
         */
        void connect(void *pAddr);

        /**
         * 扩展空间
         * 
         * @param pAddr, 已经是空间被扩展之后的地址
         * @param iSize
         */
        void append(void *pAddr, size_t iSize);

        void* getHead() const { return _pHead; }

        /**
         * 总计内存大小, 包括后续增加的内存块的大小
         *
         * @return size_t
         */
        size_t getMemSize() const { return _pHead->_iTotalSize; }

        /**
         * 用于存放数据的总计内存大小, 包括后续增加的内存块的大小
         * 
         */
        size_t getAllCapacity();

        /**
         * 每个chunk的大小, 包括后续增加的内存块的大小
         *
         * vector<size_t>
         */
        void   getSingleCapacity(vector<size_t> &vec_capacity);

        /**
         * 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个,换算成绝对地址
         * @param iPageId
         * @param iIndex
         * @return void*
         */
        void* getAbsolute(size_t iPageId, size_t iIndex);

        /**
         * 分配一个区块,绝对地址
         *
         * @param iNeedSize,需要分配的大小
         * @param iAllocSize, 分配的数据块大小
         * @return void*
         */
        void* allocate(size_t iNeedSize, size_t &iAllocSize);

        /**
         * 释放区块, 绝对地址
         * @param pAddr
         */
        void  deallocate(void* pAddr);

        /**
         * 分配一个区块
         * @param iNeedSize,需要分配的大小
         * @param iAllocSize, 分配的数据块大小
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         * @return void *
         */
        void* allocate(size_t iNeedSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);

        /**
         * 释放内存, 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个
         * @param iPageId, 该内存所属的TC_Span的起始页号
         * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
         */
        void  deallocate(size_t iPageId, size_t iIndex);

        /**
         * 重建
         */
        void rebuild();

        /**
         * 修改更新到内存中
         */
        void doUpdate(bool bUpdate = false);

        /**
         * 头部内存块
         */
        struct tagChunkAllocatorHead
        {
            bool   _bProtectedArea;
            size_t _iSize;
            size_t _iTotalSize;
            size_t _iNext;
        }__attribute__((packed));

        /**
         * 头部内存块大小
         */
        static size_t getHeadSize() { return sizeof(tagChunkAllocatorHead); }

        /**
         * 传递给此内存分配器的内存块大小要不小于函数的返回值
         */
        static size_t getNeedMinSize() { return sizeof(tagChunkAllocatorHead) + TC_Page::getMinMemSize(); }
    protected:
        void init(void *pAddr);

        void _connect(void *pAddr);

        TC_MallocChunkAllocator *lastAlloc();

        //禁止copy构造
        TC_MallocChunkAllocator(const TC_MallocChunkAllocator &);

        //禁止复制
        TC_MallocChunkAllocator& operator=(const TC_MallocChunkAllocator &);

    private:
        /**
         * 头指针
         */
        tagChunkAllocatorHead   *_pHead;
        /**
         *  chunk开始的指针
         */
        void                    *_pChunk;
        /**
         *  chunk分配类
         */
        TC_Page                 _page;
        /**
         * 后续的多块分配器
         */
        TC_MallocChunkAllocator *_nallocator;
    };
    
}
#endif

       可以看到, 这个一个封装后的内存分配器, 里面无非就是跟内存相关的各种申请, 拷贝, 释放动作。

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值