haproxy-代码阅读-内存管理

简介: haproxy内存池概述内存池按照类型分类,每个类型的内存池都有一个名字,用链表记录空闲的内存块,每个内存块大小相等,并按照16字节对齐。haporxy用pool_head 结构记录内存池struct pool_head { void **free_list; /* 空闲链表 */...

haproxy内存池概述

内存池按照类型分类,每个类型的内存池都有一个名字,用链表记录空闲的内存块,每个内存块大小相等,并按照16字节对齐。
haporxy用pool_head 结构记录内存池

struct pool_head {
    void **free_list;   /* 空闲链表 */
    struct list list;   /* 双向链表,链接每种类型的内存池 */
    unsigned int used;  /* 使用了多少内存块 */
    unsigned int allocated; /* 分配了多少内存块 */
    unsigned int limit; /* 内存块上限 */
    unsigned int minavail;  /* 最少保留几个,回收时不会全部回收 */
    unsigned int size;  /* 内存块大小 */
    unsigned int flags; /* 能否共享,类型不同,但大小相同的,能否共享一个pool_head */
    unsigned int users; /* 内存池有几个使用者 */
    char name[12];      /* 内存池名称 */
};

在程序执行过程中,产生的内存池,很有可能按照大小,排列成如下方式:
img_b4b0b975d6269bd5990b328347b493d1.png

内存池的创建

haproxy创建内存池时,会先检查内存池中,有没有与所需大小相同的内存池,有且内存池可共享,将pool_head.users++。若没有,则新创建一个内存池。

struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
{
    struct pool_head *pool;
    struct pool_head *entry;
    struct list *start;
    unsigned int align;
    
    //按照16字节对齐
    align = 16;
    size  = (size + align - 1) & -align;
    //pools是全局变量,内存池的头节点
    start = &pools;
    pool = NULL;

    
    list_for_each_entry(entry, &pools, list) {
        if (entry->size == size) {
            if (flags & entry->flags & MEM_F_SHARED) {//大小相等且可共享
                pool = entry;
                break;
            }
        }
        else if (entry->size > size) { //内存池按照大小排序,新pool_head,插在适当位置
            start = &entry->list;
            break;
        }
    }
    //创建一个新的内存池
    if (!pool) {
        pool = CALLOC(1, sizeof(*pool));
        if (!pool)
            return NULL;
        if (name)
            strlcpy2(pool->name, name, sizeof(pool->name));
        pool->size = size;
        pool->flags = flags;
        LIST_ADDQ(start, &pool->list);
    }
    pool->users++;
    return pool;
}

内存申请

create_pool仅仅是申请了内存池的类型,还没有具体分配内存,分配内存的工作由pool_refill_alloc来完成

void *pool_refill_alloc(struct pool_head *pool)
{
    void *ret;

    //如果可申请的内存块有上限,且已达上限,不再申请
    if (pool->limit && (pool->allocated >= pool->limit))
        return NULL;
    ret = MALLOC(pool->size);
    //如果申请失败,pool_gc2()垃圾回收,然后再申请一次,再失败就放弃
    if (!ret) {
        pool_gc2(); 
        ret = MALLOC(pool->size);
        if (!ret)
            return NULL;
    }
    pool->allocated++;
    pool->used++;
    return ret;
}

其中,pool_gc2()垃圾回收函数,会遍历所有内存池,并释放空闲内存块(留下minavail的数量)。
使用者申请内存,不是直接调用pool_refill_alloc,而是通过调用pool_alloc2,如果free_list中没有空闲内存了,则调用pool_refill_alloc申请下内存。如果还有空闲内存,则使用第一块内存,free_list指向下一块。

#define pool_alloc2(pool)                                     \
({                                                            \
        void *__p;                                            \
        if ((__p = pool->free_list) == NULL)                  \
                __p = pool_refill_alloc(pool);                \
        else {                                                \
                pool->free_list = *(void **)pool->free_list;  \
                pool->used++;                                 \
        }                                                     \
        __p;                                                  \
})

内存释放

如果内存块的使用者,在申请内存块后,不主动释放内存块,那么销毁内存池后,内存块将无法回到内存。所以,必须注意,要主动释放内存块。
内存块的释放很简单,将内存块直接放到空闲链表的第一个节点就行。

#define pool_free2(pool, ptr)                           \
({                                                      \
        *(void **)ptr = (void *)pool->free_list;        \
        pool->free_list = (void *)ptr;                  \
        pool->used--;                                   \
        pool_gc2_ifneed(pool);                          \
})

销毁内存池

内存池的销毁很简单,释放所有空闲内存块,然后释放内存池。如果还有使用中的内存(pool->used != 0),停止释放

void *pool_destroy2(struct pool_head *pool)
{
    if (pool) {
        pool_flush2(pool);
        if (pool->used)
            return pool;
        pool->users--;
        if (!pool->users) {
            LIST_DEL(&pool->list);
            FREE(pool);
        }
    }
    return NULL;
}

其中, pool_flush2函数会直接释放掉所有空闲内存

void pool_flush2(struct pool_head *pool)
{
    void *temp, *next;
    if (!pool)
        return;

    next = pool->free_list;
    while (next) {
        temp = next;
        next = *(void **)temp;
        pool->allocated--;
        FREE(temp);
    }
    pool->free_list = next;
}
目录
相关文章
|
9月前
|
存储 Unix Shell
软件运行机制及内存管理
软件运行机制及内存管理
121 0
|
9月前
|
存储 编译器 Linux
内存管理基础概念总述
内存管理基础概念总述
58 0
|
算法 Linux PHP
PHP是如何和Linux的CPU交互的?生命周期是怎样的?底层原理是什么?
PHP是如何和Linux的CPU交互的?生命周期是怎样的?底层原理是什么?
|
缓存 算法 NoSQL
PHP的吞吐量是什么意思?底层原理是什么?
PHP的吞吐量是什么意思?底层原理是什么?
PHP的结构化编程是什么意思?底层原理是什么?
PHP的结构化编程是什么意思?底层原理是什么?
|
Java PHP
PHP的内存管理机制是干什么的?底层原理是什么?
PHP的内存管理机制是干什么的?底层原理是什么?
109 0
|
存储 Java PHP
PHP的堆和栈使用场景是什么?底层原理是什么?
PHP的堆和栈使用场景是什么?底层原理是什么?
123 0
|
PHP UED
PHP的并发能力是什么意思?底层原理是什么?
PHP的并发能力是什么意思?底层原理是什么?
172 0
|
自然语言处理 网络协议 数据库连接
PHP内核的工作原理是什么?底层原理是什么?
PHP内核的工作原理是什么?底层原理是什么?
|
消息中间件 存储 负载均衡
PHP的分布式系统的设计原理是什么?底层原理是什么?
PHP的分布式系统的设计原理是什么?底层原理是什么?
172 0