haproxy-代码阅读-内存管理

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];		/* 内存池名称 */
};

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

内存池的创建

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;
}
posted @ 2017-05-29 10:49  抟九  阅读(1066)  评论(0编辑  收藏  举报