malloc的底层实现

时间:2021-08-26 13:38:08

// 申请内存块的类型

/* Memory block identification */

#define _FREE_BLOCK      0

#define _NORMAL_BLOCK    1

#define _CRT_BLOCK       2

#define _IGNORE_BLOCK    3

#define _CLIENT_BLOCK    4

#define _MAX_BLOCKS      5

 

#define nNoMansLandSize 4

 

// 管理内存块的数据结构

typedef struct _CrtMemBlockHeader

{

        struct _CrtMemBlockHeader * pBlockHeaderNext;

        struct _CrtMemBlockHeader * pBlockHeaderPrev;

        char *                      szFileName;

        int                         nLine;

        size_t                      nDataSize;

        int                         nBlockUse;

        long                        lRequest;

        unsigned char               gap[nNoMansLandSize];

} _CrtMemBlockHeader;


最终内存块的布局图:

malloc的底层实现

// 解析到有效数据的结构

#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))

 

// 从有效数据出找到实际申请空间的起始位置

#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1)

 

 

/*

 * Statics governing how often _CrtCheckMemory is called.

 */

static unsigned check_frequency = _CRTDBG_CHECK_DEFAULT_DF >> 16;

static unsigned check_counter;

 

static long _lRequestCurr = 1;      /* 第x次请求 */

 

extern "C" _CRTIMP long _crtBreakAlloc = -1L;  /* Break on allocation by request number */

 

static size_t _lTotalAlloc;         /* 所有申请的总字节数 */

static size_t _lCurAlloc;           /* 当前申请的总字节数 */

static size_t _lMaxAlloc;           /* 一次申请的最大字节数 */

 

// 填充申请空间的字节信息

static unsigned char _bNoMansLandFill = 0xFD;   /*有效数据部分前标记*/

static unsigned char _bAlignLandFill  = 0xED;   /*对齐字节数的标记 */

static unsigned char _bDeadLandFill   = 0xDD;   /*释放空间使用0xDD填充 */

static unsigned char _bCleanLandFill  = 0xCD;   /*申请有效数据空间使用0xCD填充 */

 

// 维护内存块的双向链表信息

static _CrtMemBlockHeader * _pFirstBlock;   // 指向链表的头

static _CrtMemBlockHeader * _pLastBlock;    //  指向链表的尾

 

// 申请内存和内存块管理的函数

extern "C" static void * __cdecl _heap_alloc_dbg_impl(

        size_t nSize,

        int nBlockUse,

        const char * szFileName,

        int nLine,

        int * errno_tmp

        )

{

        long lRequest;

        size_t blockSize;

        int fIgnore = FALSE;

        _CrtMemBlockHeader * pHead;

        void *retval=NULL;

        /* lock the heap

         */

        _mlock(_HEAP_LOCK);

        __try {

 

            /* verify heap before allocation */

            if (check_frequency > 0)

                if (check_counter == (check_frequency - 1))

                {

                    _ASSERTE(_CrtCheckMemory());

                    check_counter = 0;

                }

                else

                    check_counter++;

 

            lRequest = _lRequestCurr;

 

            /* break into debugger at specific memory allocation */

            if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)

                _CrtDbgBreak();

 

            /* forced failure */

            if ((_pfnAllocHook) && !(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, (const unsigned char *)szFileName, nLine))

            {

                if (szFileName)

                    _RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n",

                        szFileName, nLine);

                else

                    _RPT0(_CRT_WARN, "Client hook allocation failure.\n");

            }

            else

            {

                /* cannot ignore CRT allocations */

                if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&

                    !(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))

                    fIgnore = TRUE;

 

                /* Diagnostic memory allocation from this point on */

 

                                   //检测申请空间是否超过所能申请的最大字节数

                if (nSize > (size_t)(_HEAP_MAXREQ - nNoMansLandSize - sizeof(_CrtMemBlockHeader)))

                {

                    _RPT1(_CRT_ERROR, "Invalid allocation size: %Iu bytes.\n", nSize);

                    if (errno_tmp)

                    {

                        *errno_tmp = ENOMEM;

                    }

                }

                else

                {

                                            //检测块的类型,用户程序均为NORMAL_BLOCK

                    if (!_BLOCK_TYPE_IS_VALID(nBlockUse))

                    {

                        _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");

                    }

 

                                            //计算所要实际申请内存块的总字节数

                    blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;

 

                    RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));

                  

                                          //根据前面计算出的结果申请内存,实际是HeapAlloc函数申请 

                                         pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);

 

                                            //检测是否申请成功,申请失败填充错误信息

                    if (pHead == NULL)

                    {

                        if (errno_tmp)

                        {

                            *errno_tmp = ENOMEM;

                        }

                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));

                    }

                    else

                    {

 

                                                    //记录本次申请成功

                        /* commit allocation */

                        ++_lRequestCurr;

 

                        if (fIgnore)

                        {

                            pHead->pBlockHeaderNext = NULL;

                            pHead->pBlockHeaderPrev = NULL;

                            pHead->szFileName = NULL;

                            pHead->nLine = IGNORE_LINE;

                            pHead->nDataSize = nSize;

                            pHead->nBlockUse = _IGNORE_BLOCK;

                            pHead->lRequest = IGNORE_REQ;

                        }

                        else {

                                                              //修改已申请字节的一些全局信息

                            /* keep track of total amount of memory allocated */

                            if (SIZE_MAX - _lTotalAlloc > nSize)

                            {

                                _lTotalAlloc += nSize;

                            }

                            else

                            {

                                _lTotalAlloc = SIZE_MAX;

                            }

                            _lCurAlloc += nSize;

 

                            if (_lCurAlloc > _lMaxAlloc)

                            _lMaxAlloc = _lCurAlloc;

 

                                                              //将新申请的结点插入到全局双向链表当中

                            if (_pFirstBlock)

                                _pFirstBlock->pBlockHeaderPrev = pHead;

                            else

                                _pLastBlock = pHead;

 

                                                             //对结构体成员赋值

                            pHead->pBlockHeaderNext = _pFirstBlock;

                            pHead->pBlockHeaderPrev = NULL;

                            pHead->szFileName = (char *)szFileName;

                            pHead->nLine = nLine;

                            pHead->nDataSize = nSize;

                            pHead->nBlockUse = nBlockUse;

                            pHead->lRequest = lRequest;

 

                            /* link blocks together */

                            _pFirstBlock = pHead;

                        }

 

                                                    //填充申请的空间

                        /* fill in gap before and after real block */

                        memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);

                        memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);

 

                        /* fill data with silly value (but non-zero) */

                        memset((void *)pbData(pHead), _bCleanLandFill, nSize);

 

                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));

 

                                                    //获取申请空间中有效数据的存放地址

                        retval=(void *)pbData(pHead);

                    }

                }

            }

 

        }

        __finally {

            /* unlock the heap

             */

                          //解锁堆

            _munlock(_HEAP_LOCK);

        }

 

                  // 返回申请存放有效数据的实际地址

        return retval;

}

 

// 将申请的内存块插入到块管理链表的示意图

malloc的底层实现

// 内存申请的操作步骤:

1、对堆进行加锁

2、在正式申请空间之前,对堆进行校验

3、检测申请内存块的类型

4、检测内存空间是否充足,不够设置错误信息,返回NULL,否则进行5

5、检测块的类型

6、计算本次所要申请的内存块的总字节数

7、按照计算的总字节数申请内存,底层真正向堆申请空间的是HeapAlloc函数

8、检测是否申请成功,如果申请失败设置错误信息,返回NULL,否则执行9

9、修改请求次数和目前申请的总字节数

10、将新申请的内存块的新节点头插到双向链表中

11、给该结点对应的结构体赋值

12、填充空间

13、获取申请内存块中存放有效数据的真正位置

14、对堆进行解锁

15、返回有效数据区域的地址