• Apache内存池学习解析


    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    Apache内存池解析

    1.前言

    内存分配会占用时间,影响程序运行效率;特别是内存申请或销毁特别频繁的程序;内存分配会产生内存碎片,分为内部碎片和外部碎片;因为所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机制的限制,系统只能分配4的整数倍的内存,多余的内存空间称为内部碎片;外部碎片指的是还没有被分配出去(不属于任何进程),分配内存时,系统会搜索直到找到大小符合的连续内存块,因为内存分配是随机的,系统的内存被分割成无数的小片段,但有些内存因为太小了,无法分配给申请内存空间的新进程的内存空闲区域,则变成外部碎片。

    内存池的设计理念是采用内存池来占用并管理一部分系统内存,实现内存的快速申请和归还;内存池可以减少系统内存碎片的产生、提高内存管理性能;在大型的服务器中内存池的作用尤其明显。内存池的内部结构主要由数组或链表组成;

    2.Apache内存池

    2.1 Apache内存池概述

           Apache内存池的设计理念是通过建立一个内存池来实现对程序的内存管理,程序需要内存时向内存池去申请,而不是像系统直接申请,程序释放内存,不是直接还给系统,而是还给内存池去管理,内存池可以自己管理一定限额的内存,不用马上还给系统,只有超过限额时才会还给系统,当然内存池管理的内存也是从系统申请过来的,一个进程的内存池中本来是没有管理一点内存的,程序第一次向内存池申请内存,内存池向系统申请内存,程序第一次将内存还给内存池,内存池并不将内存还给系统,而是自己管理起来,内存池这才开始管理起内存。程序第二次从内存池中申请内存时,内存池可以直接将第一次归还的内存直接给程序用,不够了再向系统去申请,就这样程序在一次次归还中建立起内存池中的内存库,供程序使用,内存中管理的内存大小应该是程序的内存使用最大峰值;通过这样一种机制减少程序直接向系统反复申请释放内存所消耗的时间,提高程序的运行效率,同时也是以程序一直占用最大峰值的内存为代价;只有当内存池销毁后,才会将内存池中的内存还给系统;

           内存池实际上是一个树状的结构,有多个内存池结点组成一个总的内存池,如图2-1所示,这只是一个示意图,当然可以往下继续延伸。根内存池结点global和分配子allocator是全局变量,生命周期与程序相同,程序启动时创建,程序关闭时销毁,而其他的内存池结点的生命周期与具体的应用有关,比如对于HTTP连接而言,要创建连接内存池结点和请求内存池结点,连接内存池结点挂在根内存池下面,生命周期与HTTP连接相同,连接创建时创建,连接断开时销毁;请求内存池结点是连接内存池结点的子结点,挂在连接内存池结点下面,在请求开始时创建,请求结束时销毁,生命周期比连接内存池结点短;分配子allocator才是整个内存池中的“池”的概念,内存池中的内存都是它向系统去申请的,它像一个内存工厂,从系统中申请一定大小的内存,打包成块;内存池结点就像是一个个批发零售商,从分配子那里申请一定大小的内存块,例如一下申请一个8kb的内存结点MemeNode,然后将8kb大小的内存结点挂在内存池结点的active指针下面,一个请求中需要内存时,可能是一次需要4字节、8字节、1024字节等,就直接从active指针下的结点里获取内存,等到8kb的内存用完或者不够后,再向allocator申请下一个内存结点MemeNode挂在active后面,active形成一个环形内存结点链表,表示内存池结点中正在使用的内存结点链表。当该内存池结点销毁后,挂在active下面的内存结点归还给allocator,由allocator继续分配给其他的内存池结点,从而实现循环利用。销毁内存池结点时,该内存池结点下所有的子内存池结点都会被销毁,子内存池结点的active下面挂的内存结点也会返还给allocator;以上就是对内存池整体的描述;先有个骨架,然后再将具体的血肉填充进去。

                           

    图2-1内存池整体结构图

    2.2 Apache内存分配子

           上文中讲到分配子allocator才代表了整个内存池中“池”的概论,首先讲pfree[MAX_INDEX],这是一个内存结点指针数组,先将内存结点理解为固定大小的内存块,allocator像是内存的批发商,内存结点都是较大的具有固定大小的内存块。pfree[MAX_INDEX]数组的每个元素都指向一个内存结点MemNode的链表,提供给内存池结点申请使用,除0索引下面挂的内存结点,其他元素下面挂的内存结点大小都相同,数组索引号与每个索引号对应内存结点大小的对应关系为 内存结点大小=4K*(index+1),例如索引号为1,结点大小则为8K,索引号为2结点大小为12K,依次类推,MAX_INDEX为宏定义,一般等于20,表示数组索引号的最大值,索引号1~MAX_INDEX下面挂的结点大小相同,称为规则结点;索引号0下挂的内存结点大小都大于4K*(MAX_INDEX+1),且内存结点大小不一定相同,称为不规则结点;采用数组索引的方式是为了方便存取,增加存取速度。当申请内存时,先将申请的大小转化为index值,然后直接根据index大小去在数组的索引号下查找是否有结点,先在索引1~MAX_INDEX下查找规则结点,如果没找到,则在0索引下查找,如果index超过MAX_INDEX,则直接在0索引下查找,如果都没找到则直接从系统申请。例如需要一个12k的内存结点,索引号为2,则在pfree[2]指针下查找是否有12K的结点,如果有则返回12K结点,如果没有则在pfree[3]下找16k的结点,一直到pfree[MAX_INDEX],以最合适的结点为原则,尽量使用allocator里已经挂载的内存结点,避免从系统去申请,16K的内存块给内存池结点去支配,也不会浪费。

    分配子中CurrentMaxIndex就是用来表示pfree[MAX_INDEX]数组中最大有效索引号;MaxManageIndex表示分配子最大可以管理的内存大小,按照公式4K*(index +1)转化为index索引,以索引值表示大小,MaxManageIndex初始化为APR_ALLOCATOR_MAX_FREE_UNLIMITED,该值实际为0,表明分配子对于回收空闲结点的大小并不设门槛,意味着即使结点再大,系统也不会回收。CurrentFreeIndex表示分配子还可以继续管理的内存索引值大小;例如程序刚启动,分配子中没有管理任何内存结点,CurrentMaxIndex=0,表示pfree[MAX_INDEX]指针数组中指针都是空的,都是无效的;设置MaxManageIndex=400,即分配子最大可以管理内存4K*(400+1)=1604kb的内存。因为一开始分配子没有管理任何内存,所以CurrentFreeIndex=MaxManageIndex=400,表示可以再管理1604kb 的内存。当程序第一次申请内存时,因为分配子中全是空的,所以直接从系统去申请内存块。当程序第一次释放时,例如释放一个12kb的结点给allocator,12kb 对应的索引号为2,allocator将这个结点挂在pfree[2]指针下面,这时CurrentMaxIndex=2,表示最大有效结点在索引号2下面,指针数组中pfree[2]后面的指针都为空。CurrentFreeIndex=400-2=398,表示分配子只能管理1604kb-12kb=1592kb大小的内存,如果释放结点内存超过1592kb,则直接还给系统,不在给分配子管理;CurrentFreeIndex是程序将内存还给分配子还是给系统的一个判断标准,

    MaxManageIndex设置好后不会变化。如果程序再释放一个20kb的内存结点给分配子,index大小为4,则挂载pfree[4]指针下面。CurrentMaxIndex大小由2变为4,表示最大的可用结点的索引值是4, pfree[4]后面的指针均为空。CurrentFreeIndex=398-4=394。如果程序需要申请一个16kb的内存结点,16kb的索引值为3,索引值3小于CurrentMaxIndex的值4,所以在规则结点中找,首先根据索引值3在pfree[3]下查找,因为pfree[3]下面没有挂载任何结点,所以继续在pfree[4]下面找,刚好有一个20kb的结点,将20kb的结点摘下,返回给程序使用,但是此时需要调整CurrentMaxIndex的值位2,因为pfree[2]指针以后的指针都是空指针,下面没有挂载任何的结点。CurrentFreeIndex的值为394+4=398。CurrentFreeIndex是随着结点挂载和摘取动态变化的,将内存结点挂在数组指针下时,CurrentFreeIndex值减小,但是不能小于0,摘取内存结点给程序使用时,CurrentFreeIndex值增大。如果还的结点超过MAX_INDEX索引值,但是不超过CurrentFreeIndex,则直接挂载在索引0指针下面,如果超过CurrentFreeIndex的值398,则直接归还给系统。挂载和摘取的操作都是在数组指针下的第一个内存结点。Pcs 临界区是多线程访问时候的互斥,而owner则记录了当前分配子所属于的内存池结点。

     

    图2-2内存分配子示意图

    2.3 内存结点

    内存结点的结构如下图所示,由结点头和剩余空间组成,结点头用来管理和记录该结点,剩余空间用于程序内存分配。例如需要申请一个内存结点占用的总空间最小为8kb,剩余空间大小=8kb-结点头大小;

     

    2-3内存结点示意图

    struct MemNode

    {

           struct MemNode                   *next;//指向下一个结点指针

           struct MemNode                  **ref;//指向自己的结点指针

           unsigned int                  index;//结点大小,

           unsigned int                  free_index;//空余容量的大小,索引大小

           char                             *first_avail;//空余内存块的起始地址

           char                             *endp;//空余内存块的末尾地址

    };

    内存结点的结构如图所示,next指向下一个内存结点,形成链表,ref为指向自身的指针的指针,指向上一个结点的next指针,上一个结点的next指针指向本节点;index大小固定,表示该结点总大小的索引值;free_index大小随着结点空间使用而减小,表示剩余空间索引值的大小,active链表中会根据这个值进行排序,大的在前,小的在后;first_avail指针指向可用空间的头部,endp指向可用空间的尾部;

    2.4 active活动链表

     

    图2-4active中内存结点连接方式

           Active活动链表挂载着内存池结点使用的内存块,按照free_index大小逆序排列,申请内存先判断active第一个结点够不够用,不够再向内存池分配子申请大块内存;内存池结点所在的内存块也会参与内存分配,是active的第一个内存结点块;

    2.5 Appach内存池的优缺点

    优点:

    (1)       内存池开始为空,在内存申请和归还的过程中建立起内存池管理的链表,按需分配,不是一开始就分配一堆;

    (2)       内存池管理的结点可以循环利用,减少直接向系统申请和释放,节省了系统的开销,提高程序性能;

    (3)       内存池可以设置上限,内存池不会占用超过限额的内存;内存池按照内存池结点来管理内存,每个内存池结点对应一个有固定生命周期的应用,应用结束,销毁内存池结点,应用中占用的内存都会归还给内存池,不会有内存泄露的问题,但前提是内存池结点要正确的创建和销毁;

    缺点:

    (1)       内存池管理的空闲内存和程序正在使用的内存之和为程序使用内存的最大峰值,而且程序会一直占用最大峰值内存,内存池从系统申请的内存会一直增大到上限,超过上限时,将超过的部分归还给系统。内存池中管理的空闲内存无法在长期闲置的情况下归还给系统。

    (2)       Active指向的链表中,结点的排序是按照index来排序,排序的最小单位是4kb的大小粒度,粒度太大,如果第二个结点只剩3byte,后面的结点都是4095byte,他们的index都相同,但是因为第二个节点为3byte,其后的所有结点的内存都无法被分配,造成内存空间的浪费。

    (3)       是按照程序中不同应用的生命周期来建立内存池结点,例如一个http连接,通过内存池结点来给http连接中用到的内存进行分配,则在该http连接的整个生命周期内,占用的内存都无法归还给内存池;内存池的创建和销毁也是非常麻烦,要考虑内存池结点的生命周期。如果内存池结点没有销毁,则该内存池结点下挂载的内存都会泄露。

    (4)       内存池有多个内存池结点,内存池结点之间公用一个锁,在多线程的程序中,锁竞争也会影响内存申请释放的性能。

    #ifndef _POOL_MEMORY_
    #define _POOL_MEMORY_
    
    #include <malloc.h>
    
    #define MEM_ALIGN(size,boundary) (((size) + ((boundary)-1)) & ~((boundary)-1))
    #define MEM_ALIGN_DEFAULT(size) MEM_ALIGN(size,8)
    #define BOUNDARY_INDEX    12
    #define MAX_INDEX        20
    
    #include "../WinAsyncIODef.h"
    
    struct _MEMNODE
    {
        struct _MEMNODE            *next;//指向下一个结点指针
        struct _MEMNODE            **ref;//指向自己的结点指针
        unsigned int            index;//结点大小
        unsigned int            free_index;//空余容量的大小
        char                    *first_avail;//空余内存块的起始地址
        char                    *endp;//空余内存块的末尾地址
    };
    
    struct _ALLOCATOR
    {
        unsigned int            max_index;
        unsigned int            max_free_index;
        unsigned int            current_free_index;
        CRITICAL_SECTION        *pcs;
        struct _MEMPOOL            *owner;
        struct _MEMNODE            *pfree[MAX_INDEX];
    };
    
    struct _CLEANUP
    {
        struct _CLEANUP            *next;
        const void                *data;
        CLEANUP_FUNC            cleanup_func;
    };
    
    struct _MEMPOOL 
    {
        struct _MEMPOOL            *parent;
        struct _MEMPOOL            *child;
        struct _MEMPOOL            *sibling;
        struct _MEMPOOL            **ref;
    
        struct _CLEANUP            *cleanups;
        struct _CLEANUP            *free_cleanups;
    
        struct _ALLOCATOR        *allocator;
        struct _MEMNODE            *active;
    };
    
    BOOL Allocator_Create(ALLOCATOR **allocator);
    void Allocator_Destroy(ALLOCATOR *allocator);
    
    inline void        Allocator_CS_Set(ALLOCATOR *allocator,CRITICAL_SECTION *pCS){ allocator->pcs = pCS; }
    inline CRITICAL_SECTION* Allocator_CS_Get(ALLOCATOR *allocator){ return allocator->pcs; }
    
    inline void        Allocator_owner_Set(ALLOCATOR *allocator,MEMPOOL *pool){ allocator->owner = pool; }
    inline MEMPOOL* Allocator_owner_Get(ALLOCATOR *allocator){ return allocator->owner;}
    
    void Allocator_max_free_Set(ALLOCATOR *allocator,size_t in_size);
    
    MEMNODE *Allocator_alloc(ALLOCATOR *allocator, size_t in_size);
    
    BOOL Pool_Create(MEMPOOL **newpool,MEMPOOL *parent,ALLOCATOR *allocator = NULL);
    void Pool_Destroy(MEMPOOL *pool);
    
    void * Mem_Alloc(MEMPOOL *pool, size_t in_size);
    
    void Pool_Cleanup_Register(MEMPOOL *p, const void *data,CLEANUP_FUNC cleanup_fnc);
    
    BOOL CriticalSection_Create(CRITICAL_SECTION **ppCS,MEMPOOL *pool);
    
    BOOL Pool_Initialize(void);
    void Pool_Terminate(void);
    
    #endif //_POOL_MEMORY_
    #include "stdafx.h"
    #include "MemoryPool.h"
    
    #define node_free_space(node_) ((size_t)(node_->endp - node_->first_avail))
    
    #define list_remove(node) do {        
        *node->ref = node->next;        
        node->next->ref = node->ref;    
    } while (0)
    
    #define list_insert(node, point) do {           
        node->ref = point->ref;                     
        *node->ref = node;                          
        node->next = point;                         
        point->ref = &node->next;                   
    } while (0)
    
    
    const unsigned int BOUNDARY_SIZE = 1 << BOUNDARY_INDEX;    //4096
    const unsigned int MIN_ALLOC = 2 * BOUNDARY_SIZE;        //8192
    const unsigned int SIZEOF_MEMNODE = MEM_ALIGN_DEFAULT(sizeof(MEMNODE));
    const unsigned int SIZEOF_ALLOCATOR = MEM_ALIGN_DEFAULT(sizeof(ALLOCATOR));
    const unsigned int SIZEOF_MEMPOOL = MEM_ALIGN_DEFAULT(sizeof(MEMPOOL));
    
    static int            pools_initialized    = 0;
    static ALLOCATOR    *global_allocator    = NULL;
    static MEMPOOL        *global_pool        = NULL;
    
    static int thread_mutex_cleanup(void *data)
    {
        CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)data;
    
        DeleteCriticalSection(pcs);
    
        return true;
    }
    
    static void Run_Cleanups(CLEANUP **cref)
    {
        CLEANUP *c = *cref;
    
        while (c) 
        {
            *cref = c->next;
            c->cleanup_func((void*)c->data);
            c = *cref;
        }
    }
    
    BOOL Allocator_Create(ALLOCATOR **allocator)
    {
        ALLOCATOR *new_allocator;
    
        if (allocator == NULL)
            return FALSE;
    
        if ((new_allocator = (ALLOCATOR*)malloc(SIZEOF_ALLOCATOR)) == NULL)
            return FALSE;
    
        memset(new_allocator, 0, SIZEOF_ALLOCATOR);
    
        *allocator = new_allocator;
    
        return TRUE;
    }
    
    void Allocator_Destroy(ALLOCATOR *allocator)
    {
        unsigned int index;
        MEMNODE *node, **ref;
    
        for (index = 0; index < MAX_INDEX; index++) 
        {
            ref = &allocator->pfree[index];
            while ( (node = *ref) ) 
            {
                *ref = node->next;
                free(node);
            }
        }
    
        free(allocator);
    }
    /********************************************************************
        function:设置内存池能够管理的最大内存容量
        input:
        ALLOCATOR *allocator,   内存分配子 
        size_t in_size                        设置最大内存容量
        problem:/bclladd如果第二次设置的比第一次设置的小,
        占用的已经超过第二次设置的总量,则allocator->current_free_index会是个负数
        
    *********************************************************************/
    void Allocator_max_free_Set(ALLOCATOR *allocator,size_t in_size)
    {
        unsigned int max_free_index;
        unsigned int size = in_size;
    
        CRITICAL_SECTION *pcs;
    
        pcs = Allocator_CS_Get(allocator);
        if (pcs)
            EnterCriticalSection(pcs);
    
    
        max_free_index = MEM_ALIGN(size, BOUNDARY_SIZE) >> BOUNDARY_INDEX;//先将size转化为与4096倍数最接近的整数,在除以4096,得到索引值
        //bclladd如果第二次设置的比第一次设置的小,占用的已经超过第二次设置的总量,则allocator->current_free_index会是个负数
        allocator->current_free_index += max_free_index;//max_free_index是内存池管理的最大内存数,
        allocator->current_free_index -= allocator->max_free_index;//oldleft=oldall-oldocupy;oldleft=oldall-oldocupy+newall-oldall=newall-oldocupy=newleft
        allocator->max_free_index = max_free_index;
        if (allocator->current_free_index > max_free_index)//负值赋值给无符号整型变量,会是一个很大的整数
            allocator->current_free_index = max_free_index;
    
    
        if (pcs)
            LeaveCriticalSection(pcs);
    }
    
    /********************************************************************
        function:分配内存
        input:
        ALLOCATOR *allocator,   内存分配子 分离出结点,挂在内存池active结点下
        size_t in_size                        需要分配的大小
        output:
        返回结点指针
        problem:
        
    *********************************************************************/
    MEMNODE *Allocator_alloc(ALLOCATOR *allocator, size_t in_size)
    {
        MEMNODE *node, **ref;
        unsigned int max_index;
        size_t size, i, index;
    
        //4096的倍数,不满8192,则分配8192
        size = MEM_ALIGN(in_size + SIZEOF_MEMNODE, BOUNDARY_SIZE);//将in_size和结点大小之和转化为大于8k的且是4k的最小倍数
        if (size < in_size)    return NULL;
        if (size < MIN_ALLOC)    size = MIN_ALLOC;
    
        //将大小转为下标
        index = (size >> BOUNDARY_INDEX) - 1;//转换成与分配子中数组对应的索引,所以要减1,index表示需要的最小的数组索引,否则链表中的内存块将小于需要的大小
    
        //小于当前最大节点
        if (index <= allocator->max_index) //小于当前最大有效索引,就是索引下有链表可分配内存
        {
            if (allocator->pcs)
                EnterCriticalSection(allocator->pcs);
    
            //找到第一个可用的不为空链表
            max_index = allocator->max_index;
            ref = &allocator->pfree[index];
            i = index;
            while (*ref == NULL && i < max_index) //寻找最适合的数组索引
            {
               ref++;
               i++;
            }
    
            //找到一个非空的链表
            if ( (node = *ref) ) 
            {
                if ((*ref = node->next) == NULL && i >= max_index) //如果可用的节点是max_index索引的数组元素下的结点,且结点后面没有node,说明最大的可用max_index失效,需要调整为当前可用
                //*ref = node->next数组元素指向note的下个结点,如果为空,且i = max_index,说明链表只有一个结点,被拿走后,该索引下的链表无效,需重新调整max_index
                {//当前链表没有后续node,并且拿走的node属于当前最大索引节点,则调整最大索引节点
                    do 
                    {
                        ref--;
                        max_index--;
                    }
                    while (*ref == NULL && max_index > 0);
    
                    allocator->max_index = max_index;
                }
    
                allocator->current_free_index += node->index + 1;//将管理的内存分出去,那么当前可管理的内存增大,node->index 等于结点大小/4k-1,所以这里加1,表示将要分配(node->index + 1)*4k大小的内存
                if (allocator->current_free_index > allocator->max_free_index)//判断分配后是否超过最大管理内存
                    allocator->current_free_index = allocator->max_free_index;
    
                if (allocator->pcs)
                    LeaveCriticalSection(allocator->pcs);
    
                node->next = NULL;//将结点取走后,next指针赋值为空。
                node->first_avail = (char *)node + SIZEOF_MEMNODE;
    
                return node;
            }
    
            if (allocator->pcs)
                LeaveCriticalSection(allocator->pcs);
        }
        //大于当前最大节点,则用0索引 如果它不为空
        else if (allocator->pfree[0]) 
        {
            if (allocator->pcs)
                EnterCriticalSection(allocator->pcs);
    
            //找到第一个满足大小的不为空节点
            ref = &allocator->pfree[0];//指向数组 第0个元素
            while (  (node = *ref)  && index > node->index)//因为排序过,所以只判断第一个结点是否满足
                ref = &node->next;//ref移向下一个结点,这里一般不会执行
    
            if (node) //找到
            {
                *ref = node->next;//数组指针指向链表的下一个结点
    
                allocator->current_free_index += node->index + 1;
                if (allocator->current_free_index > allocator->max_free_index)
                    allocator->current_free_index = allocator->max_free_index;
    
                if (allocator->pcs)
                    LeaveCriticalSection(allocator->pcs);
    
    
                node->next = NULL;
                node->first_avail = (char *)node + SIZEOF_MEMNODE;
    
                return node;
            }
    
            if (allocator->pcs)
                LeaveCriticalSection(allocator->pcs);
    
        }
    
        //什么都没找到,直接申请
        if ((node = (MEMNODE*)malloc(size)) == NULL)
            return NULL;
    
        node->next = NULL;
        node->index = index;
        node->first_avail = (char *)node + SIZEOF_MEMNODE;
        node->endp = (char *)node + size;
    
        return node;
    }
    /********************************************************************
        function:将内存返回给内存池
        input:
        ALLOCATOR *allocator,   内存分配子 
        MEMNODE *node                       需要返回的结点
        output:
        
        problem:
        
    *********************************************************************/
    void Allocator_free(ALLOCATOR *allocator, MEMNODE *node)
    {
        MEMNODE *next, *freelist = NULL;
        unsigned int index, max_index;
        unsigned int max_free_index, current_free_index;
    
        if (allocator->pcs)
            EnterCriticalSection(allocator->pcs);
    
        max_index = allocator->max_index;
        max_free_index = allocator->max_free_index;
        current_free_index = allocator->current_free_index;
    
    
    
        do //
        {
            next = node->next;
            index = node->index;
    
            if (max_free_index != 0//有内存容量限制
                && index + 1 > current_free_index) //超过剩余能管理的内存大小
            {//已经超过最大容量,归还给系统
                node->next = freelist;
                freelist = node;
            }
            else if (index < MAX_INDEX) 
            {//小于80K,属于规则节点(1-19) 
                if ((node->next = allocator->pfree[index]) == NULL//将node插入到pfree[index]链表头
                    && index > max_index) //大于当前最大有效索引
                {
                    max_index = index;//调整最大可用索引
                }
                allocator->pfree[index] = node;//pfree[index]重新保存头结点
                if (current_free_index >= (index + 1))//node内存又重新挂到内存池上,当前可管理的内存剩余量减小,
                    current_free_index -= (index + 1);
                else
                    current_free_index = 0;
            }
            else 
            {//大于80K,挂到0索引中
                node->next = allocator->pfree[0];
                allocator->pfree[0] = node;
                if (current_free_index >= (index + 1))
                    current_free_index -= (index + 1);
                else
                    current_free_index = 0;
            }
        } while ( (node = next)  );
    
        allocator->max_index = max_index;
        allocator->current_free_index = current_free_index;
    
        if (allocator->pcs)
            LeaveCriticalSection(allocator->pcs);
    
    
        while (freelist != NULL) 
        {
            node = freelist;
            freelist = node->next;
            free(node);
        }
    }
    
    BOOL Pool_Create(MEMPOOL **newpool,MEMPOOL *parent,ALLOCATOR *allocator /* = NULL */)
    {
        MEMPOOL *pool;
        MEMNODE *node;
    
        if (newpool==NULL)//指向指针的指针所以不为空
            return FALSE;
    
        if (parent == NULL)
            parent = global_pool;//全局pool为父内存池,
    
        if (allocator == NULL)//第一次创建时不为空
            allocator = parent->allocator;//子内存池和父内存池使用同一个内存分配子
    
    //分配一个node来保存MEMPOOL
        if ((node = Allocator_alloc(allocator,MIN_ALLOC - SIZEOF_MEMNODE)) == NULL) 
            return FALSE;
    
        node->next = node;
        node->ref = &node->next;//指向自己的ref
    
        pool = (MEMPOOL *)node->first_avail;//pool指向node起始有效地址
        node->first_avail = (char *)pool + SIZEOF_MEMPOOL;//调整node有效地址下移,node先保存MEMNODE,再保存MEMPOOL,剩余内存空间继续分配
    
        pool->allocator        = allocator;//子内存池的allocator指针指向父内存分配子
        pool->active        = node;//保存内存池结构的node也放在active链表中,内存池atcvie指向node
        pool->child            = NULL;
        pool->cleanups        = NULL;
        pool->free_cleanups = NULL;
    
    
        if (  (pool->parent = parent) ) 
        {
            CRITICAL_SECTION *pcs;
    
            if ( (pcs = Allocator_CS_Get(parent->allocator)) )
                EnterCriticalSection(pcs);
    
    
    
            if ( (pool->sibling = parent->child) )//将原来的子内存池parent->child赋值给新建内存池的兄弟指针
                pool->sibling->ref = &pool->sibling;//兄弟指针的自身指针指向自己
    
            parent->child = pool;//将新建的pool赋值给 parent->child 
            pool->ref = &parent->child;//将新建pool的自身指向指针ref 赋值为 &parent->child,即每新建一个内存池都保存到child里,而将原来的child内存池放到pool->sibling 链表中
    
    
            if (pcs)
                LeaveCriticalSection(pcs);
    
        }
        else 
        {
            pool->sibling = NULL;
            pool->ref = NULL;
        }
    
        *newpool = pool;//返回新创建的内存池
    
        return TRUE;
    }
    
    void Pool_Destroy(MEMPOOL *pool)
    {
        MEMNODE *active;
        ALLOCATOR *allocator;
    
        while (pool->child)
            Pool_Destroy(pool->child);
    
        Run_Cleanups(&pool->cleanups);
    
        if (pool->parent) 
        {
    
            CRITICAL_SECTION *pcs;
    
            if ( (pcs = Allocator_CS_Get(pool->parent->allocator)) )
                EnterCriticalSection(pcs);
    
    
            if ( (*pool->ref = pool->sibling) )
                pool->sibling->ref = pool->ref;
    
            if (pcs)
                LeaveCriticalSection(pcs);
    
        }
    
        
        allocator = pool->allocator;
        active = pool->active;
        *active->ref = NULL;
    
    
        if (Allocator_owner_Get(allocator) == pool)
        { 
            Allocator_CS_Set(allocator, NULL);
        }
    
        Allocator_free(allocator, active);
    
        
        if (Allocator_owner_Get(allocator) == pool) 
        {
            Allocator_Destroy(allocator);
        }
    }
    
    /*********************************
    从active链表中分配内存,如果不够,则在分配子分配结点
    ***********************************/
    void * Mem_Alloc(MEMPOOL *pool, size_t in_size)
    {
        MEMNODE *active, *node;
        void *mem;
        size_t size, free_index;
    
        size = MEM_ALIGN_DEFAULT(in_size);        //最接近的不小于8的倍数
        if (size < in_size) return NULL;
    
        active = pool->active;
    
        //如果第一个结点有剩余空间,直接使用
        if (size <= node_free_space(active)) 
        {
            mem = active->first_avail;
            active->first_avail += size;
    
            memset(mem,0,in_size);
            return mem;
        }
    //第一个结点剩余空间不够采用第二个结点
        node = active->next;//第二个结点剩余空间是最大的,
        if (size <= node_free_space(node)) //够了,
        {
            list_remove(node);//摘下结点供使用
        }
        else 
        {//还是不够用则后面的肯定也不够用,因为剩余空间大小是排过序的,所以要从分配子重新分配结点
            if ((node = Allocator_alloc(pool->allocator, size)) == NULL)//分配子是按大小要求分配结点的 
                return NULL;
        }
    
        node->free_index = 0;    
    
        mem = node->first_avail;
        node->first_avail += size;//给mem分配空间
    
        list_insert(node, active);//将剩余的空间结点再插入到链表头,
    
        pool->active = node;//node放在active中,作为链表头
    
        free_index = (MEM_ALIGN(active->endp - active->first_avail + 1,
            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;    //最近的小于4096的倍数
    
        active->free_index = free_index;
        node = active->next;//node指向active的第二个结点
        if (free_index >= node->free_index)//如果第一个结点剩余空间比第二结点还大,说明不用排序,直接返回
        {
            memset(mem,0,in_size);
            return mem;
        }
    
        //如果第一个结点分配完后剩余的容量小于第二个结点,需按剩余空间从大到小排序
        do 
        {
            node = node->next;
        }
        while (free_index < node->free_index);//从大到小排列,找到插入位置
    
        list_remove(active);//取出链表中第一个结点,即刚才分配一部分内存出去后的结点
        list_insert(active, node);//在node位置插入active结点
    
        memset(mem,0,in_size);
        return mem;
    }
    
    void Pool_Cleanup_Register(MEMPOOL *p, const void *data,CLEANUP_FUNC cleanup_fnc)
    {
        CLEANUP *c;
    
        if ( p ) 
        {
            if (p->free_cleanups) 
            {
                c = p->free_cleanups;
                p->free_cleanups = c->next;
            } 
            else 
            {
                c =(CLEANUP *) Mem_Alloc(p, sizeof(CLEANUP));
            }
            c->data = data;
            c->cleanup_func = cleanup_fnc;
            
            c->next = p->cleanups;
            p->cleanups = c;
        }
    }
    
    BOOL CriticalSection_Create(CRITICAL_SECTION **ppCS,MEMPOOL *pool)
    {
        (*ppCS) = (CRITICAL_SECTION *)Mem_Alloc(pool, sizeof(**ppCS));
    
        InitializeCriticalSection(*ppCS);
    
        Pool_Cleanup_Register(pool, (*ppCS), thread_mutex_cleanup);
        return TRUE;
    }
    
    BOOL Pool_Initialize(void)
    {
        if (pools_initialized++)//唯一,如果已经初始化则pools_initialized≥1,直接返回
            return TRUE;
    
        if (Allocator_Create(&global_allocator) == FALSE) //创建分配子,并全部初始化为0
        {
            pools_initialized = 0;
            return FALSE;
        }
    
        if ( Pool_Create(&global_pool, NULL,global_allocator) == FALSE) //创建父内存池
        {
            Allocator_Destroy(global_allocator);
            global_allocator = NULL;
            pools_initialized = 0;
            return FALSE;
        }
    
        CRITICAL_SECTION* pcs;
    
        if ( CriticalSection_Create(&pcs,global_pool) == FALSE) 
        {
            return FALSE;
        }
    
        Allocator_CS_Set(global_allocator, pcs);
    
        Allocator_owner_Set(global_allocator, global_pool);
    
        return TRUE;
    }
    
    void Pool_Terminate(void)
    {
        if (pools_initialized==0)
            return;
    
        if (--pools_initialized)
            return;
    
        Pool_Destroy(global_pool); /* This will also destroy the mutex */
        global_pool = NULL;
    
        global_allocator = NULL;
    }

    详细请参考大神的博客

    http://blog.csdn.net/klinymao/article/details/54783409

  • 相关阅读:
    MVVM绑定 填坑,必须在与 DependencyObject 相同的线程上创建 DependencySource
    备份一个迭代查找TreeViewItem的辅助函数
    备份一个集合分组的算法
    备份一个有的时候,可能需要把其它exe或者dll包含在主程序中....
    wpf 代码判断当前是否在设计模式,兼容没有UI线程的限制
    wpf 打开输入法、禁用输入法
    xunit vs2019扩展
    .net core使用nacos作为配置中心
    使用skywalking跟踪你的请求
    帮你做codereview:使用docker安装sonarqube并扫描你的.net core元源码
  • 原文地址:https://www.cnblogs.com/bclshuai/p/6856268.html
Copyright © 2020-2023  润新知