GlusterFS之内存池(mem-pool)实现原理及代码

系统 1436 0

我的新浪微博: http://weibo.com/freshairbrucewoo

欢迎大家相互交流,共同提高技术。

最近一直在研究glusterfs的源代码,自己也在上面做了一些小的改动。我最开始研究的是3.2.5这个版本,因为据同行和网上资料显示这个版本目前是最稳定的版本。glusterfs实现比较复杂,具体的设计思想和架构就不详细介绍了,网上有这方面的资料(CSDN博客里面就有很好介绍的文章)。

        研究开源系统的一个好处就是可以充分了解它的实现,如果是看这方面的论文只能了解一些原理性的东西,但是我们真正做项目还需要实际的实现。很多开源系统可能本身不一定就很适合你的系统,但是如果可以改造那么利用它来改造也是很值得划算的。研究开源系统最大的好处就是学习它的优秀的代码,今天这篇博文就是要分享glusterfs里面使用的内存池技术。

        glusterfs实现内存池技术的源文件和头文件分别是mem-pool.c和mem-pool.h,首先看看头文件中内存池对象结构体的定义如下:

      
         1
      
      
        struct
      
      
         mem_pool {  


      
      
         2
      
      
        struct
      
       list_head  list;
      
        //
      
      
        用于管理内存池的标准双向链表  
      
      
         3
      
      
        int
      
                     hot_count;
      
        //
      
      
        正在使用的内存数量计数  
      
      
         4
      
      
        int
      
                     cold_count;
      
        //
      
      
        未使用的内存数量计数  
      
      
         5
      
               gf_lock_t         
      
        lock
      
      
        ;  


      
      
         6
      
               unsigned 
      
        long
      
           padded_sizeof_type;
      
        //
      
      
        带有填充  
      
      
         7
      
      
        void
      
                   *pool;
      
        //
      
      
        内存池开始地址  
      
      
         8
      
      
        void
      
                   *pool_end;
      
        //
      
      
        内存池结束地址  
      
      
         9
      
      
        int
      
                     real_sizeof_type;
      
        //
      
      
        内存池存放对象的真实大小  
      
      
        10
      
               uint64_t          alloc_count;
      
        //
      
      
        采用alloc分配的次数  
      
      
        11
      
               uint64_t          pool_misses;
      
        //
      
      
        内出池缺少次数  
      
      
        12
      
      
        int
      
                     max_alloc;
      
        //
      
      
        采用alloc分配的最大次数  
      
      
        13
      
      
        int
      
      
                       curr_stdalloc;  


      
      
        14
      
      
        int
      
      
                       max_stdalloc;  


      
      
        15
      
      
        char
      
                   *
      
        name;  


      
      
        16
      
      
        struct
      
       list_head  global_list;
      
        //
      
      
        加入到全局的内存池链表  
      
      
        17
      
       };  
    

 


然后我们在来分析几个重要的实现函数,第一个函数就是mem_pool_new_fn,它会新建一个内存池对象,然后按照传递进来的内存的大小和个数分配内存,还要加上一些额外存储内容的内存容量,如存放链表指针的因为这些内存池对象本身是通过通用链表来管理的,还有如标识内存是否在被使用的一个标志等。具体看下面代码的实现,关键代码都有注释:

 

      
         1
      
      
        struct
      
       mem_pool *  


      
         2
      
       mem_pool_new_fn (unsigned 
      
        long
      
      
         sizeof_type,  


      
      
         3
      
                        unsigned 
      
        long
      
       count, 
      
        char
      
       *
      
        name)  


      
      
         4
      
      
        {  


      
      
         5
      
      
        struct
      
       mem_pool  *mem_pool =
      
         NULL;  


      
      
         6
      
               unsigned 
      
        long
      
           padded_sizeof_type = 
      
        0
      
      
        ;  


      
      
         7
      
      
        void
      
                   *pool =
      
         NULL;  


      
      
         8
      
      
        int
      
                     i = 
      
        0
      
      
        ;  


      
      
         9
      
      
        int
      
                     ret = 
      
        0
      
      
        ;  


      
      
        10
      
      
        struct
      
       list_head *list =
      
         NULL;  


      
      
        11
      
               jdfs_ctx_t  *ctx =
      
         NULL;  


      
      
        12
      
      
        13
      
      
        if
      
       (!sizeof_type || !
      
        count) {  


      
      
        14
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      , GF_LOG_ERROR, 
      
        "
      
      
        invalid argument
      
      
        "
      
      
        );  


      
      
        15
      
      
        return
      
      
         NULL;  


      
      
        16
      
      
                }  


      
      
        17
      
               padded_sizeof_type = sizeof_type + GF_MEM_POOL_PAD_BOUNDARY;
      
        //
      
      
        计算大小:对象本身所占内存+链表头+内存池指针+int内存大小(存放in_use变量)  
      
      
        18
      
      
        19
      
               mem_pool = GF_CALLOC (
      
        sizeof
      
       (*mem_pool), 
      
        1
      
      
        , gf_common_mt_mem_pool);  


      
      
        20
      
      
        if
      
       (!
      
        mem_pool)  


      
      
        21
      
      
        return
      
      
         NULL;  


      
      
        22
      
      
        23
      
               ret = gf_asprintf (&mem_pool->name, 
      
        "
      
      
        %s:%s
      
      
        "
      
      , THIS->name, name);
      
        //
      
      
        哪一个xlator分配什么名字内存  
      
      
        24
      
      
        if
      
       (ret < 
      
        0
      
      
        )  


      
      
        25
      
      
        return
      
      
         NULL;  


      
      
        26
      
      
        27
      
      
        if
      
       (!mem_pool->
      
        name) {  


      
      
        28
      
      
                        GF_FREE (mem_pool);  


      
      
        29
      
      
        return
      
      
         NULL;  


      
      
        30
      
      
                }  


      
      
        31
      
      
        32
      
               LOCK_INIT (&mem_pool->
      
        lock
      
      
        );  


      
      
        33
      
               INIT_LIST_HEAD (&mem_pool->
      
        list);  


      
      
        34
      
               INIT_LIST_HEAD (&mem_pool->
      
        global_list);  


      
      
        35
      
      
        36
      
               mem_pool->padded_sizeof_type = padded_sizeof_type;
      
        //
      
      
        总的对齐内存大小  
      
      
        37
      
               mem_pool->cold_count = count;
      
        //
      
      
        数量:刚开始都是冷的(未使用的)  
      
      
        38
      
               mem_pool->real_sizeof_type = sizeof_type;
      
        //
      
      
        使用内存池对象的真实内存大小  
      
      
        39
      
      
        40
      
               pool = GF_CALLOC (count, padded_sizeof_type, gf_common_mt_long);
      
        //
      
      
        分配count个padded_sizeof_type大小的内存  
      
      
        41
      
      
        if
      
       (!
      
        pool) {  


      
      
        42
      
                       GF_FREE (mem_pool->
      
        name);  


      
      
        43
      
      
                        GF_FREE (mem_pool);  


      
      
        44
      
      
        return
      
      
         NULL;  


      
      
        45
      
      
                }  


      
      
        46
      
      
        47
      
      
        for
      
       (i = 
      
        0
      
      ; i < count; i++
      
        ) {  


      
      
        48
      
                       list = pool + (i * (padded_sizeof_type));
      
        //
      
      
        分配每一个内存对象大小到链表  
      
      
        49
      
      
                        INIT_LIST_HEAD (list);  


      
      
        50
      
                       list_add_tail (list, &mem_pool->list);
      
        //
      
      
        加入到内存池的链表中去  
      
      
        51
      
      
                }  


      
      
        52
      
      
        53
      
               mem_pool->pool = pool;
      
        //
      
      
        记录分配的内存区域  
      
      
        54
      
               mem_pool->pool_end = pool + (count * (padded_sizeof_type));
      
        //
      
      
        内存分配结束的地址  
      
      
        55
      
      
        56
      
      
        /*
      
      
         add this pool to the global list 
      
      
        */
      
      
        57
      
               ctx =
      
         jdfs_ctx_get ();  


      
      
        58
      
      
        if
      
       (!
      
        ctx)  


      
      
        59
      
      
        goto
      
      
        out
      
      
        ;  


      
      
        60
      
      
        61
      
               list_add (&mem_pool->global_list, &ctx->mempool_list);
      
        //
      
      
        加入全局的内存池链表  
      
      
        62
      
      
        63
      
      
        out
      
      
        :  


      
      
        64
      
      
        return
      
      
         mem_pool;  


      
      
        65
      
       }  
    

 

 

如果我们需要使用这种内存池中的内存,那么就从内存池中拿出一个对象(不同对象需要不同的内存池对象保存,每一个内存池对象只保存一种对象的内存结构)的内存,代码实现和注释如下:

 

      
         1
      
      
        void
      
       *  


      
         2
      
       mem_get (
      
        struct
      
       mem_pool *
      
        mem_pool)  


      
      
         3
      
      
        {  


      
      
         4
      
      
        struct
      
       list_head *list =
      
         NULL;  


      
      
         5
      
      
        void
      
                   *ptr =
      
         NULL;  


      
      
         6
      
      
        int
      
                   *in_use =
      
         NULL;  


      
      
         7
      
      
        struct
      
       mem_pool **pool_ptr =
      
         NULL;  


      
      
         8
      
      
         9
      
      
        if
      
       (!
      
        mem_pool) {  


      
      
        10
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      , GF_LOG_ERROR, 
      
        "
      
      
        invalid argument
      
      
        "
      
      
        );  


      
      
        11
      
      
        return
      
      
         NULL;  


      
      
        12
      
      
                }  


      
      
        13
      
      
        14
      
               LOCK (&mem_pool->
      
        lock
      
      
        );  


      
      
        15
      
      
                {  


      
      
        16
      
                       mem_pool->alloc_count++
      
        ;  


      
      
        17
      
      
        if
      
       (mem_pool->cold_count) {
      
        //
      
      
        内存池中是否还有未使用的内存对象  
      
      
        18
      
                               list = mem_pool->list.next;
      
        //
      
      
        取出一个  
      
      
        19
      
                               list_del (list);
      
        //
      
      
        从链表中脱链  
      
      
        20
      
      
        21
      
                               mem_pool->hot_count++
      
        ;  


      
      
        22
      
                               mem_pool->cold_count--
      
        ;  


      
      
        23
      
      
        24
      
      
        if
      
       (mem_pool->max_alloc < mem_pool->hot_count)
      
        //
      
      
        最大以分配的内存是否小于正在使用的内存数量  
      
      
        25
      
                                       mem_pool->max_alloc = mem_pool->
      
        hot_count;  


      
      
        26
      
      
        27
      
                               ptr =
      
         list;  


      
      
        28
      
                               in_use = (ptr + GF_MEM_POOL_LIST_BOUNDARY +  


      
        29
      
                                         GF_MEM_POOL_PTR);
      
        //
      
      
        分配内存池对象的时候分配了这个区域来保存次块内存是否在使用  
      
      
        30
      
                               *in_use = 
      
        1
      
      ;
      
        //
      
      
        标记次块内存正在使用  
      
      
        31
      
      
        32
      
      
        goto
      
      
         fwd_addr_out;  


      
      
        33
      
      
                        }  


      
      
        34
      
      
        35
      
      
        /*
      
      
         This is a problem area. If we've run out of 


      
      
        36
      
      
                         * chunks in our slab above, we need to allocate 


      
      
        37
      
      
                         * enough memory to service this request. 


      
      
        38
      
      
                         * The problem is, these individual chunks will fail 


      
      
        39
      
      
                         * the first address range check in __is_member. Now, since 


      
      
        40
      
      
                         * we're not allocating a full second slab, we wont have 


      
      
        41
      
      
                         * enough info perform the range check in __is_member. 


      
      
        42
      
      
                         * 


      
      
        43
      
      
                         * I am working around this by performing a regular allocation 


      
      
        44
      
      
                         * , just the way the caller would've done when not using the 


      
      
        45
      
      
                         * mem-pool. That also means, we're not padding the size with 


      
      
        46
      
      
                         * the list_head structure because, this will not be added to 


      
      
        47
      
      
                         * the list of chunks that belong to the mem-pool allocated 


      
      
        48
      
      
                         * initially. 


      
      
        49
      
      
                         * 


      
      
        50
      
      
                         * This is the best we can do without adding functionality for 


      
      
        51
      
      
                         * managing multiple slabs. That does not interest us at present 


      
      
        52
      
      
                         * because it is too much work knowing that a better slab 


      
      
        53
      
      
                         * allocator is coming RSN. 


      
      
        54
      
      
        */
      
      
        55
      
                       mem_pool->pool_misses++;
      
        //
      
      
        内存池缺失计数次数加1  
      
      
        56
      
                       mem_pool->curr_stdalloc++;
      
        //
      
      
        系统标准分配次数加1  
      
      
        57
      
      
        if
      
       (mem_pool->max_stdalloc < mem_pool->
      
        curr_stdalloc)  


      
      
        58
      
                               mem_pool->max_stdalloc = mem_pool->
      
        curr_stdalloc;  


      
      
        59
      
                       ptr = GF_CALLOC (
      
        1
      
      , mem_pool->
      
        padded_sizeof_type,  


      
      
        60
      
                                        gf_common_mt_mem_pool);
      
        //
      
      
        分配一个内存池对象  
      
      
        61
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      , GF_LOG_DEBUG, 
      
        "
      
      
        Mem pool is full. 
      
      
        "
      
      
        62
      
      
        "
      
      
        Callocing mem
      
      
        "
      
      
        );  


      
      
        63
      
      
        64
      
      
        /*
      
      
         Memory coming from the heap need not be transformed from a 


      
      
        65
      
      
                         * chunkhead to a usable pointer since it is not coming from 


      
      
        66
      
      
                         * the pool. 


      
      
        67
      
      
        */
      
      
        68
      
      
                }  


      
      
        69
      
      
        fwd_addr_out:  


      
      
        70
      
               pool_ptr =
      
         mem_pool_from_ptr (ptr);  


      
      
        71
      
               *pool_ptr = (
      
        struct
      
       mem_pool *
      
        )mem_pool;  


      
      
        72
      
               ptr = mem_pool_chunkhead2ptr (ptr);
      
        //
      
      
        得到真正开始的内存  
      
      
        73
      
               UNLOCK (&mem_pool->
      
        lock
      
      
        );  


      
      
        74
      
      
        75
      
      
        return
      
      
         ptr;  


      
      
        76
      
       }  
    

 

当我们使用完一个内存池中的内存结构以后就需要还给内存池以便被以后的程序使用,达到循环使用的目的。但是在归还以前我们首先需要判断是不是内存池对象的一个成员,判断的结果有三种,分别是:是,不是和错误情况(就是它在内存池的内存范围以内,但是不符合内存池对象的大小),实现如下:

 

      
         1
      
      
        static
      
      
        int
      
      
         2
      
       __is_member (
      
        struct
      
       mem_pool *pool, 
      
        void
      
       *ptr)
      
        //
      
      
        判断ptr指向的内存是否是pool的成员  
      
      
         3
      
      
        {  


      
      
         4
      
      
        if
      
       (!pool || !
      
        ptr) {  


      
      
         5
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      , GF_LOG_ERROR, 
      
        "
      
      
        invalid argument
      
      
        "
      
      
        );  


      
      
         6
      
      
        return
      
       -
      
        1
      
      
        ;  


      
      
         7
      
      
                }  


      
      
         8
      
      
         9
      
      
        if
      
       (ptr < pool->pool || ptr >= pool->pool_end)
      
        //
      
      
        ptr如果不再pool开始到结束的范围内就不是  
      
      
        10
      
      
        return
      
      
        0
      
      
        ;  


      
      
        11
      
      
        12
      
      
        if
      
       ((mem_pool_ptr2chunkhead (ptr) - pool->
      
        pool)  


      
      
        13
      
                   % pool->padded_sizeof_type)
      
        //
      
      
        判断是否是一个符合内存块大小的内存对象  
      
      
        14
      
      
        return
      
       -
      
        1
      
      
        ;  


      
      
        15
      
      
        16
      
      
        return
      
      
        1
      
      
        ;  


      
      
        17
      
       }  
    

 

那么根据上面函数判断的结果,放入内存对象到内存池对象的函数就会做相应的处理,具体代码如下:

 

      
         1
      
      
        void
      
      
         2
      
       mem_put (
      
        void
      
       *ptr)
      
        //
      
      
        将ptr放回到内存池中去  
      
      
         3
      
      
        {  


      
      
         4
      
      
        struct
      
       list_head *list =
      
         NULL;  


      
      
         5
      
      
        int
      
          *in_use =
      
         NULL;  


      
      
         6
      
      
        void
      
         *head =
      
         NULL;  


      
      
         7
      
      
        struct
      
       mem_pool **tmp =
      
         NULL;  


      
      
         8
      
      
        struct
      
       mem_pool *pool =
      
         NULL;  


      
      
         9
      
      
        10
      
      
        if
      
       (!
      
        ptr) {  


      
      
        11
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      , GF_LOG_ERROR, 
      
        "
      
      
        invalid argument
      
      
        "
      
      
        );  


      
      
        12
      
      
        return
      
      
        ;  


      
      
        13
      
      
                }  


      
      
        14
      
      
        15
      
               list = head = mem_pool_ptr2chunkhead (ptr);
      
        //
      
      
        得到链表指针  
      
      
        16
      
               tmp =
      
         mem_pool_from_ptr (head);  


      
      
        17
      
      
        if
      
       (!
      
        tmp) {  


      
      
        18
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      
        , GF_LOG_ERROR,  


      
      
        19
      
      
        "
      
      
        ptr header is corrupted
      
      
        "
      
      
        );  


      
      
        20
      
      
        return
      
      
        ;  


      
      
        21
      
      
                }  


      
      
        22
      
      
        23
      
               pool = *
      
        tmp;  


      
      
        24
      
      
        if
      
       (!
      
        pool) {  


      
      
        25
      
                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      
        , GF_LOG_ERROR,  


      
      
        26
      
      
        "
      
      
        mem-pool ptr is NULL
      
      
        "
      
      
        );  


      
      
        27
      
      
        return
      
      
        ;  


      
      
        28
      
      
                }  


      
      
        29
      
               LOCK (&pool->
      
        lock
      
      
        );  


      
      
        30
      
      
                {  


      
      
        31
      
      
        32
      
      
        switch
      
      
         (__is_member (pool, ptr))  


      
      
        33
      
      
                        {  


      
      
        34
      
      
        case
      
      
        1
      
      :
      
        //
      
      
        是内存池中的内存  
      
      
        35
      
                               in_use = (head + GF_MEM_POOL_LIST_BOUNDARY +  


      
        36
      
                                         GF_MEM_POOL_PTR);
      
        //
      
      
        得到是否正在使用变量  
      
      
        37
      
      
        if
      
       (!is_mem_chunk_in_use(in_use)) {
      
        //
      
      
        正在使用就暂时不回收  
      
      
        38
      
                                       gf_log_callingfn (
      
        "
      
      
        mem-pool
      
      
        "
      
      
        , GF_LOG_CRITICAL,  


      
      
        39
      
      
        "
      
      
        mem_put called on freed ptr %p of mem 
      
      
        "
      
      
        40
      
      
        "
      
      
        pool %p
      
      
        "
      
      
        , ptr, pool);  


      
      
        41
      
      
        break
      
      
        ;  


      
      
        42
      
      
                                }  


      
      
        43
      
                               pool->hot_count--
      
        ;  


      
      
        44
      
                               pool->cold_count++
      
        ;  


      
      
        45
      
                               *in_use = 
      
        0
      
      
        ;  


      
      
        46
      
                               list_add (list, &pool->list);
      
        //
      
      
        加入到内存池中的链表  
      
      
        47
      
      
        break
      
      
        ;  


      
      
        48
      
      
        case
      
       -
      
        1
      
      :
      
        //
      
      
        错误就终止程序  
      
      
        49
      
      
        /*
      
      
         For some reason, the address given is within 


      
      
        50
      
      
                                 * the address range of the mem-pool but does not align 


      
      
        51
      
      
                                 * with the expected start of a chunk that includes 


      
      
        52
      
      
                                 * the list headers also. Sounds like a problem in 


      
      
        53
      
      
                                 * layers of clouds up above us. ;) 


      
      
        54
      
      
        */
      
      
        55
      
      
                                abort ();  


      
      
        56
      
      
        break
      
      
        ;  


      
      
        57
      
      
        case
      
      
        0
      
      :
      
        //
      
      
        不是内存池中的内存直接释放掉  
      
      
        58
      
      
        /*
      
      
         The address is outside the range of the mem-pool. We 


      
      
        59
      
      
                                 * assume here that this address was allocated at a 


      
      
        60
      
      
                                 * point when the mem-pool was out of chunks in mem_get 


      
      
        61
      
      
                                 * or the programmer has made a mistake by calling the 


      
      
        62
      
      
                                 * wrong de-allocation interface. We do 


      
      
        63
      
      
                                 * not have enough info to distinguish between the two 


      
      
        64
      
      
                                 * situations. 


      
      
        65
      
      
        */
      
      
        66
      
                               pool->curr_stdalloc--;
      
        //
      
      
        系统分配次数减1  
      
      
        67
      
      
                                GF_FREE (list);  


      
      
        68
      
      
        break
      
      
        ;  


      
      
        69
      
      
        default
      
      
        :  


      
      
        70
      
      
        /*
      
      
         log error 
      
      
        */
      
      
        71
      
      
        break
      
      
        ;  


      
      
        72
      
      
                        }  


      
      
        73
      
      
                }  


      
      
        74
      
               UNLOCK (&pool->
      
        lock
      
      
        );  


      
      
        75
      
       }  
    

 

除了上面介绍的,当然还有销毁内存池的功能函数mem_pool_destroy,辅助分配系统内存的一些封装函数等;另外还有一个对于调试有用的功能,那就是记录分配内存的信息,这些东西相对简单,可以自己直接看源码理解。

 

下班了,今天就到此为止吧!以后准备分享iobuf实现的原理以及源代码!

GlusterFS之内存池(mem-pool)实现原理及代码详解


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论