在本文,将用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内存池
/*
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);
}