内存池及其实现

在本文,将用C语言实现不同类型的内存池,它们的原型分别来自于Linux内核的mempool、mysql的my_alloc、nginx的ngx_palloc。为了能在脱离原工程后简单的使用这些内存池,对原来的代码进行了稍微的修改。下面的正文中,只给出了部分代码,完整的代码可从http://download.csdn.net/detail/it_pcode/6633733处下载

linux的mempool

 mempool一次性申请N个大小为M的内存块,在你使用过程中并不释放。mempool内存池,实际上是后备内存,只有在内存紧张时,在申请内存资源失败时,才会从内存池中获取所需的内存。在创建内存池时,需要自己定义内存的申请和释放函数,并内存池内存块的最小个数。

创建内存池

/**
 * mempool_create - create a memory pool
 * @min_nr:    the minimum number of elements guaranteed to be
 *             allocated for this pool.
 * @alloc_fn:  user-defined element-allocation function.
 * @free_fn:   user-defined element-freeing function.
 * @pool_data: optional private data available to the user-defined functions.
 *
 * this function creates and allocates a guaranteed size, preallocated
 * memory pool. The pool can be used from the mempool_alloc() and mempool_free()
 * functions. This function might sleep. Both the alloc_fn() and the free_fn()
 * functions might sleep - as long as the mempool_alloc() function is not called
 * from IRQ contexts.
 */
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
		mempool_free_t *free_fn, void *pool_data) {
	return mempool_create_node(min_nr, alloc_fn, free_fn, pool_data, -1);
}

/*该函数进行实际的内存池创建工作*/
mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
		mempool_free_t *free_fn, void *pool_data, int node_id) {
	mempool_t *pool;
	pool = malloc(sizeof(*pool));
	if (!pool)
		return NULL;
	pool->elements = malloc(min_nr * sizeof(void *));
	if (!pool->elements) {
		free(pool);
		return NULL;
	}
	pool->min_nr = min_nr;
	pool->pool_data = pool_data;
	pool->alloc = alloc_fn;
	pool->free = free_fn;

	/*
	 * First pre-allocate the guaranteed number of buffers.
	 */
	while (pool->curr_nr < pool->min_nr) {
		void *element;

		element = pool->alloc(pool->pool_data);
		if (!element) {
			free_pool(pool);
			return NULL;
		}
		add_element(pool, element);
	}
	return pool;
}

销毁内存池

/**
 * mempool_destroy - deallocate a memory pool
 * @pool:      pointer to the memory pool which was allocated via
 *             mempool_create().
 *
 * this function only sleeps if the free_fn() function sleeps. The caller
 * has to guarantee that all elements have been returned to the pool (ie:
 * freed) prior to calling mempool_destroy().
 */
void mempool_destroy(mempool_t *pool) {
	free_pool(pool);
}

static void free_pool(mempool_t *pool) {
	while (pool->curr_nr) {
		void *element = remove_element(pool);
		pool->free(element, pool->pool_data);
	}
	free(pool->elements);
	free(pool);
}

申请内存

/**
 * mempool_alloc - allocate an element from a specific memory pool
 * @pool:      pointer to the memory pool which was allocated via
 *             mempool_create().
 * @gfp_mask:  the usual allocation bitmask.
 *
 * this function only sleeps if the alloc_fn() function sleeps or
 * returns NULL. Note that due to preallocation, this function
 * *never* fails when called from process contexts. (it might
 * fail if called from an IRQ context.)
 */
void * mempool_alloc(mempool_t *pool, int mask) {
	void *element;

	element = pool->alloc(pool->pool_data);
	if (element != NULL)
		return element;

	if (pool->curr_nr > 0) {
		element = remove_element(pool);
		return element;
	}
	return NULL;
}

释放内存

/**
 * mempool_free - return an element to the pool.
 * @element:   pool element pointer.
 * @pool:      pointer to the memory pool which was allocated via
 *             mempool_create().
 *
 * this function only sleeps if the free_fn() function sleeps.
 */
void mempool_free(void *element, mempool_t *pool) {
	if (element == NULL)
		return;

	if (pool->curr_nr < pool->min_nr) {
		if (pool->curr_nr < pool->min_nr) {
			add_element(pool, element);
			return;
		}
	}
	pool->free(element, pool->pool_data);
}

内存池使用实例

void * alloc_fn(void * pool_data) {
	int *data = (int *) pool_data;
	return malloc(*data);
}

void free_fn(void *elem, void * pool_data) {
	free(elem);
}

void test_mempool() {
	mempool_t *pool = NULL;
	int d = 10;
	char *str;
	pool = mempool_create(20, alloc_fn, free_fn, &d);
	str = (char *) mempool_alloc(pool, 0);
	if (NULL == str)
		printf("cannot get memory for mempool_alloc for str\n");
	strcpy(str, "hello\n");
	puts(str);
	mempool_free(str, pool);
	puts(str);
}

 mysql内存池

mysql内部使用的内存管理程序,可以实现多次申请内存块, 中途任何时刻失败, 或者下次使用前释放内存, 无需再关心每次申请和释放了哪些内存.
工作原理:
初始化定义每次分配的最小内存块大小M,如果申请一次内存, 大小为X, X大于M, 就分配一块X的内存, 加入到管理链表中.如果小于的话, 看之前剩余的还够不够, 如果足够的话, 返回之前多余的内存地址.如果不够,则申请这么大的内存, 也计入链表中。
释放是一次性的,也可以不释放内存,而是标记已经使用的内存为“未使用”,下次同样的应用可以继续使用。
创建内存池
/*
 Initialize memory root

 SYNOPSIS
 init_alloc_root()
 mem_root       - memory root to initialize
 block_size     - size of chunks (blocks) used for memory allocation
 (It is external size of chunk i.e. it should include
 memory required for internal structures, thus it
 should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE)
 pre_alloc_size - if non-0, then size of block that should be
 pre-allocated during memory root initialization.

 DESCRIPTION
 This function prepares memory root for further use, sets initial size of
 chunk for memory allocation and pre-allocates first block if specified.
 Altough error can happen during execution of this function if
 pre_alloc_size is non-0 it won't be reported. Instead it will be
 reported as error in first alloc_root() on this memory root.
 */

void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
		uint pre_alloc_size __attribute__((unused))) {
	DBUG_ENTER("init_alloc_root");
	mem_root->free = mem_root->used = mem_root->pre_alloc = 0;
	mem_root->min_malloc = 32;
	mem_root->block_size = block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
	mem_root->error_handler = 0;
	mem_root->block_num = 4; /* We shift this with >>2 */
	mem_root->first_block_usage = 0;

	if (pre_alloc_size) {
		if ((mem_root->free = mem_root->pre_alloc = (USED_MEM*) my_malloc(
				pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)),
				MYF(0))))
				{
					mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM));
      mem_root->free->left= pre_alloc_size;
      mem_root->free->next= 0;
    }
	}

	DBUG_VOID_RETURN;
}
申请内存
gptr alloc_root(MEM_ROOT *mem_root, unsigned int Size) {
	uint get_size, block_size;
	gptr point;
	reg1 USED_MEM *next = 0;
	reg2 USED_MEM **prev;
	DBUG_ENTER("alloc_root");DBUG_ASSERT(alloc_root_inited(mem_root));

	Size = ALIGN_SIZE(Size);
	if ((*(prev = &mem_root->free)) != NULL)
	{
		if ((*prev)->left < Size
				&& mem_root->first_block_usage++
						>= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP
				&& (*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
				{
			next = *prev;
			*prev = next->next; /* Remove block from list */
			next->next = mem_root->used;
			mem_root->used = next;
			mem_root->first_block_usage = 0;
		}
		for (next = *prev; next && next->left < Size; next = next->next)
			prev = &next->next;
	}
	if (!next) { /* Time to alloc new block */
		block_size = mem_root->block_size * (mem_root->block_num >> 2);
		get_size = Size + ALIGN_SIZE(sizeof(USED_MEM));
		get_size = max(get_size, block_size);

		if (!(next = (USED_MEM*) my_malloc(get_size, MYF(MY_WME)))) {
			if (mem_root->error_handler)
				(*mem_root->error_handler)();
			return ((gptr) 0); /* purecov: inspected */
		}
		mem_root->block_num++;
		next->next = *prev;
		next->size = get_size;
		next->left = get_size - ALIGN_SIZE(sizeof(USED_MEM));
		*prev = next;
	}

	point = (gptr) ((char*) next + (next->size - next->left));
	/*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
	if ((next->left -= Size) < mem_root->min_malloc) { /* Full block */
		*prev = next->next; /* Remove block from list */
		next->next = mem_root->used;
		mem_root->used = next;
		mem_root->first_block_usage = 0;
	}DBUG_RETURN(point);
}
释放内存
/*
 Deallocate everything used by alloc_root or just move
 used blocks to free list if called with MY_USED_TO_FREE

 SYNOPSIS
 free_root()
 root		Memory root
 MyFlags		Flags for what should be freed:

 MY_MARK_BLOCKS_FREED	Don't free blocks, just mark them free
 MY_KEEP_PREALLOC	If this is not set, then free also the
 preallocated block

 NOTES
 One can call this function either with root block initialised with
 init_alloc_root() or with a bzero()-ed block.
 It's also safe to call this multiple times with the same mem_root.
 */

void free_root(MEM_ROOT *root, myf MyFlags) {
	reg1 USED_MEM *next, *old;
	DBUG_ENTER("free_root");

	if (!root) /* QQ: Should be deleted */
		DBUG_VOID_RETURN; /* purecov: inspected */
	if (MyFlags & MY_MARK_BLOCKS_FREE)
	{
		mark_blocks_free(root);
		DBUG_VOID_RETURN;
	}
	if (!(MyFlags & MY_KEEP_PREALLOC))
		root->pre_alloc = 0;

	for (next = root->used; next;) {
		old = next;
		next = next->next;
		if (old != root->pre_alloc)
			my_free((gptr) old, MYF(0));
	}
	for (next = root->free; next;) {
		old = next;
		next = next->next;
		if (old != root->pre_alloc)
			my_free((gptr) old, MYF(0));
	}
	root->used = root->free = 0;
	if (root->pre_alloc) {
		root->free = root->pre_alloc;
		root->free->left = root->pre_alloc->size - ALIGN_SIZE(sizeof(USED_MEM));
		TRASH_MEM(root->pre_alloc);
		root->free->next = 0;
	}
	root->block_num = 4;
	root->first_block_usage = 0;
	DBUG_VOID_RETURN;
}
内存池使用实例
void test_myalloc() {
	MEM_ROOT root;
	char *str;
	init_alloc_root(&root, 1 << 12, 1 << 10);
	str = (char *) alloc_root(&root, 1024 * sizeof(double));
	if (NULL == str)
		printf("cannot get memory for alloc root for str\n");
	strcpy(str, "hello\n");
	puts(str);
	//mark free, can be used again
	free_root(&root, MY_MARK_BLOCKS_FREE);
	//free, can not be used
	free_root(&root, 0);
}

 nginx内存池

nginx对内存的管理分为大内存与小内存,当某一个申请的内存大于某一个值时,就需要从大内存中分配空间,否则从小内存中分配空间。

nginx中的内存池是在创建的时候就设定好了大小,在以后分配小块内存的时候,如果内存不够,则是重新创建一块内存串到内存池中,而不是将原有的内存进行扩充。当要分配大块内存时,则是在内存池外面再分配空间进行管理的,称为大块内存池。

Nginx内存池中大内存块和小内存块的分配与释放是不一样的,我们在使用内存池时,可以使用ngx_palloc进行分配,使用ngx_pfree释放。而对于大内存,这样做是没有问题的,而对于小内存就不一样了,分配的小内存,不会进行释放。因为大内存块的分配只对前3个内存块进行检查,否则就直接分配内存,所以大内存块的释放必须及时。小内存是在销毁内存池的时候释放的。

创建内存池

ngx_pool_t *
ngx_create_pool(size_t size) {
	ngx_pool_t *p;
	ngx_log_t *log = NULL;

	p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
	if (p == NULL) {
		return NULL;
	}

	p->d.last = (u_char *) p + sizeof(ngx_pool_t);
	p->d.end = (u_char *) p + size;
	p->d.next = NULL;
	p->d.failed = 0;

	size = size - sizeof(ngx_pool_t);
	p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

	p->current = p;
	p->chain = NULL;
	p->large = NULL;
	p->cleanup = NULL;
	p->log = log;

	return p;
}

销毁内存池

void ngx_destroy_pool(ngx_pool_t *pool) {
	ngx_pool_t *p, *n;
	ngx_pool_large_t *l;
	ngx_pool_cleanup_t *c;

	for (c = pool->cleanup; c; c = c->next) {
		if (c->handler) {
			c->handler(c->data);
		}
	}

	for (l = pool->large; l; l = l->next) {
		if (l->alloc) {
			ngx_free(l->alloc);
		}
	}

	for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
		ngx_free(p);

		if (n == NULL) {
			break;
		}
	}
}

申请内存

void *
ngx_palloc(ngx_pool_t *pool, size_t size) {
	u_char *m;
	ngx_pool_t *p;

	if (size <= pool->max) {

		p = pool->current;

		do {
			m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);

			if ((size_t) (p->d.end - m) >= size) {
				p->d.last = m + size;

				return m;
			}

			p = p->d.next;

		} while (p);

		return ngx_palloc_block(pool, size);
	}

	return ngx_palloc_large(pool, size);
}

释放内存

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p) {
	ngx_pool_large_t *l;

	for (l = pool->large; l; l = l->next) {
		if (p == l->alloc) {
			ngx_free(l->alloc);
			l->alloc = NULL;

			return NGX_OK;
		}
	}

	return NGX_DECLINED;
}


内存池使用实例

void test_ngxpalloc(){
	ngx_pool_t * pool = ngx_create_pool(1024);
	char *str;
	str = (char *) ngx_alloc(100);
	if (NULL == str)
			printf("cannot get memory for mempool_alloc for str\n");
		strcpy(str, "hello\n");
		puts(str);
		ngx_int_t res = ngx_pfree(pool, str);
		printf("free result is %d\n", res);
		puts(str);
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值