您的位置:首页 > 理论基础 > 数据结构算法

一个简单的内存池的实现

2008-11-02 08:17 363 查看
   

一个简单的内存池的实现

当频繁地用malloc申请内存,然后再用free释放内存时,会存在两个主要问题。第一个问题是频繁的分配释放内存可能导致系统内存碎片过多;第二个问题是分配释放内存花费的时间可能比较多(这个问题不太明显)。这个时候我们就可以考虑使用内存池了。

最朴素的内存池思想就是,首先你向系统申请一块很大的内存(这块内存因为很大,以致于我们常称它为memory pool),然后你在上面实现类似于malloc和free等操作。当你需要分配内存时,你用自己的类malloc函数从内存池上取一小块给使用者(后文称之为小内存块),当用类free函数释放从内存池上取得的内存时,这个小内存块也并不归还给系统,而只是还给内存池。

最近写telnet还原程序,写了个简易内存池,和大家分享下。

这个内存池提供这样的功能:i)提供类malloc函数mem_alloc从内存池上获取固定大小的内存,提供类free函数mem_free释放内存到内存池中;ii)内存池初始大小为某个固定大小(BUF_SIZE)的n倍,当内存池中的内存不够用时,内存池能以一定步长增长直到无系统内存。

要实现这些功能,首先应该用一个数据结构(mem_node_t)将内存池中的各个小内存块挂起来;然后为了使内存池能够以一定步长增长,我们要能分配多个大的内存块(即多个小内存池),为了能顺利的管理各个大的内存块,需要一个数据结构(mem_block_t)来记录这些信息;最后,内存池的当前状态(包括可分配的空闲链表,空闲的小内存块的个数等)是我们感兴趣的东西,所以用数据结构(mem_pool_t)来记录。

typedef union
_mem_node

{

       union _mem_node *next;

       char buf[BUF_SIZE];

}mem_node_t,
*pmem_node_t;

 

typedef struct
_mem_block

{

       mem_node_t *node_head; /* 第一个小内存块 */

       mem_node_t *node_tail;  /* 最后一个小内存块 */

       int node_cnt; /* node count */

       struct _mem_block *next;

}mem_block_t,
*pmem_block_t;

 

typedef struct
_mem_pool

{

       mem_block_t *block_head; /* 第一个大内存块 */

       mem_block_t *block_tail; /* 最后一个大内存块 */

       mem_node_t *free_head; /* 当前空闲链表的头指针 */

       int block_cnt; /* block count */

       int free_cnt; /* free node count; */

       int base; /* 内存池的初始大小 */

       int step; /* 内存池的增长步长 */

}mem_pool_t,
*pmem_pool_t;

 

然后提供了一些操作函数:mem_pool_init用来初始化内存池;mem_pool_destroy用来释放内存池,将内存池所占空间归还系统;print_mem_pool_info用来打印内存池的信息;mem_alloc,用来从内存池上分配小内存块;mem_free,将小内存块归还给内存池。

mem_alloc和mem_free操作的就是内存池的空闲链表,前者从空闲链表取一个结点,后者将一个结点插入空闲链表。而这个空闲链表是由mem_pool_init初始化的,而且当内存池增长时,即增加新的大内存块时,我们将大内存块上的小内存块也挂接到这个空闲链表上来。需要注意的是小内存块的数据结构用了联合,这是因为小内存块要么是挂接在空闲链表上,要么是分配给了用户,必居且只能居这两种状态之一,这个数据结构可根据需求适当改进。

在码代码之前,要补充说明的是,不少内存池都提供了分配不同大小的内存块的功能,将多个不同大小的本内存池链接起来也可实现这些功能,改动是比较容易的^_^

最后,本内存池在vc6下验证当频繁分配释放100字节大小的内存时,本内存池效率约是直接malloc和free的10倍,当分配大小变小时,效率比有所降低,但当分配大小增大时,效率比有所升高,分配1000字节大小的内存时,效率比约为100。

代码。

/************************mem_pool.h************************

*author:bripengandre

*

**********************************************************/

#ifndef _MEM_POOL_H_

#define _MEM_POOL_H_

 

#define BUF_SIZE  100

#define BASE_COUNT 10000

#define STEP_COUNT 1000

/* #pragma pack () */

 

/* 
*/

typedef union _mem_node

{

         union
_mem_node *next;

         char
buf[BUF_SIZE];

}mem_node_t, *pmem_node_t;

 

/* used to store block information */

typedef struct _mem_block

{

         mem_node_t
*node_head;

         mem_node_t
*node_tail;

         int
node_cnt; /* node count */

         struct
_mem_block *next;

}mem_block_t, *pmem_block_t;

 

/* used to store the pool information */

typedef struct _mem_pool

{

         mem_block_t
*block_head;

         mem_block_t
*block_tail;

         mem_node_t
*free_head;

         int
block_cnt; /* block count */

         int
free_cnt; /* free node count; */

         int
base;

         int
step;

}mem_pool_t, *pmem_pool_t;

 

 

/* 
mem_pool will have at least base blocks, and will increase steps a time
if needed */

int mem_pool_init(int base, int step);

void mem_pool_destroy(void);

void print_mem_pool_info(void);

/* since the block size is constant, this
function need no input parameter */

void *mem_alloc(void);

void mem_free(void *ptr);

 

#endif /*
_MEM_POOL_H */

/************************mem_pool.c************************

*author:bripengandre

*

**********************************************************/

#include "mem_pool.h"

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

 #define MEM_POOL_DEBUG

 

/* add new memory block to our memory pool
*/

static int add_mem_block(int cnt);

/* init the new block */

static int mem_block_init(int cnt,
mem_block_t *block);

/* init free_list of the new block */

static int free_list_init(const mem_block_t
*block);

 

 

static mem_pool_t mem_pool;

 

/* 
mem_pool will have at least base blocks, and will increase steps a time
if needed */

int mem_pool_init(int base, int step)

{      

         if(base
<= 0)

    {

    base = BASE_COUNT;

    }

   
if(step <= 0)

    {

    step = STEP_COUNT;

    }

   

   
/* initiate mem_pool */

   
memset(&mem_pool, 0, sizeof(mem_pool));

   
mem_pool.base = base;

   
mem_pool.step = step;

 

   
/* add the base block(node of base count) into the memory pool */

   
if(!add_mem_block(base))

    {

    fprintf(stderr,
"mem_pool_init::add_mem_block error/n");

    return 0;

    }

   
return 1;

}

 

 

void mem_pool_destroy(void)

{

         mem_block_t
*prev, *cur;

        

         prev
= NULL;

         cur
= mem_pool.block_head;

         while(prev
!= NULL)

         {

                   prev
= cur;

                   cur
= cur->next;

                   free(cur->node_head);

                   free(prev);

         }

        

         memset(&mem_pool,
0, sizeof(mem_pool_t));

}

 

 

void print_mem_pool_info(void)

{

         int
i;

         mem_block_t
*p;

        

         if(mem_pool.block_head
== NULL)

         {

                   fprintf(stderr,
"memory pool has been created!/n");

                   return;

         }

         printf("***************memory
pool information start***********************/n");

         printf("block
count: %4d/n", mem_pool.block_cnt);

         printf("current
free node count: %4d/n", mem_pool.free_cnt);

         printf("base
block size: %4d/n", mem_pool.base);

         printf("increasing
block size: %4d/n", mem_pool.step);

         printf("the
first block: %#x/n", mem_pool.block_head);

         printf("the
last block: %#x/n", mem_pool.block_tail);

         printf("the
first free node: %#x/n/n", mem_pool.free_head);

         for(p
= mem_pool.block_head, i = 0; p != NULL; p = p->next, i++)

         {

                   printf("-------------------block
%4d--------------------------/n", i+1);

                   printf("node
count: %4d/n", p->node_cnt);

                   printf("the
first node: %#x/n", p->node_head);

                   printf("the
last node: %#x/n", p->node_tail);

                   printf("------------------------------------------------------/n");

         }

         printf("************************memory
pool information end**************************************/n");

}

 

 

/* since the block size is constant, this
function need no input parameter */

void *mem_alloc(void)

{

         mem_node_t
*p;

        

         /*
no free node ready, attempt to allocate new free node */

         if(mem_pool.free_head
== NULL)

         {

                   if(!add_mem_block(mem_pool.step))

                   {

                            return
NULL;

                   }

         }

        

         /*
get free node from free_list */

         p
= mem_pool.free_head;

         mem_pool.free_head
= p->next;

         /*
decrease the free node count */

         mem_pool.free_cnt--;

         return
p;

}

 

 

void mem_free(void *ptr)

{

         if(ptr
== NULL)

         {

                   return;

         }      

         /*
return the node to free_list */

         ((mem_node_t
*)ptr)->next = mem_pool.free_head;

         mem_pool.free_head
= ptr;

         /*
increase the free node count */

         mem_pool.free_cnt++;

}

 

 

/* add new memory block to our memory pool
*/

static int add_mem_block(int cnt)

{

         mem_block_t
*block;

        

         if(
(block = malloc(sizeof(mem_block_t))) == NULL)

         {

                   fprintf(stderr,
"add_mem_block::malloc error/n");

                   return
0;

         }                

        

         memset(block,
0, sizeof(mem_block_t));

         if(!mem_block_init(cnt,
block))

    {

    fprintf(stderr,
"mem_pool_init::mem_block_init error/n");

    return 0;

    }

   
/* insert the new block in the head */

   
block->next = mem_pool.block_head;

   
mem_pool.block_head = block;

   
if(mem_pool.block_tail == NULL)

    {

    mem_pool.block_tail = block;

    }

   

   
/* insert the new block into the free list */

   
block->node_tail->next = mem_pool.free_head;

   
mem_pool.free_head = block->node_head;

   
mem_pool.free_cnt += cnt;

   
/* increase the block count */

   
mem_pool.block_cnt++;   

   
return 1;

}

 

 

/* init the new block */

static int mem_block_init(int cnt,
mem_block_t *block)

{

         int
size;

         mem_node_t
*p;

          

         if(block
== NULL)

         {

                   return
0;

         }

        

   
size = cnt*sizeof(mem_node_t);

   
if( (p = malloc(size)) == NULL)

    {

    fprintf(stderr, "mem_pool_init::malloc
error/n");

    return 0;

    }

   
memset(p, 0, size);

   
memset(block, 0, sizeof(mem_block_t));

   
block->node_cnt = cnt;

   
block->node_head = p;

   
block->node_tail = p+cnt-1;

   
free_list_init(block);

   
return 1;

}

 

 

/* init free_list of the new block */

static int free_list_init(const mem_block_t
*block)

{

         mem_node_t *p, *end;

        

         if(block == NULL)

         {

                   return
0;

         }

 

         /*
start initiating free list */

         end
= block->node_tail; /* block_cnt > 0 */

         for(p
= block->node_head; p < end; p++)

         {

                   p->next
= (p+1);

         }

         p->next
= NULL; /* end->next = NULL */

         return
1;

}

 

 

#ifdef MEM_POOL_DEBUG

 

#define ALLOC_COUNT 10

 

void alloc_test(char *ptr[])

{

         int
i, j;

 

         for(i
= 0; i < ALLOC_COUNT; i++)

         {

                   if(
(ptr[i] = mem_alloc()) == NULL)

                   {

                            fprintf(stderr,
"mem_alloc error/n");

                            return;

                   }

                   for(j
= 0; j < ALLOC_COUNT; j++)

                   {

                            ptr[i][j]
= 'a' + j;

                   }

         }

         for(i
= 0; i < ALLOC_COUNT; i++)

         {

                   for(j
= 0; j < ALLOC_COUNT; j++)

                   {

                            printf("ptr[%d][%d]=%c  ", i, j, ptr[i][j]);

                   }

                   fputc('/n',
stdout);

         }

}

 

 

int main(int argc, char *argv[])

{

         int
base, step;

         char
*ptr1[ALLOC_COUNT], *ptr2[ALLOC_COUNT];

 

         switch(argc)

         {

                   case
1:

                            base
= 0; /* default count */

                            step
= 0; /* default count */

                            break;

                   case
2:

                            base
= atoi(argv[1]);

                            step
= 0;

                            break;

                   case
3:

                            base
= atoi(argv[1]);

                            step
= atoi(argv[2]);

                            break;

                   default:

                            fprintf(stderr,
"Usage: %s [<base> [step]]/n", argv[0]);

                            break;

         }

        

         if(!mem_pool_init(base,
step))

         {

                   fprintf(stderr,
"mem_pool_init error/n");

                   return
1;

         }

         print_mem_pool_info();

         alloc_test(ptr1);

         print_mem_pool_info();

        

         mem_free(ptr1[5]);

         print_mem_pool_info();

        

         alloc_test(ptr2);

         print_mem_pool_info();

        

         mem_pool_destroy();

        

        

         /*
once again */

         if(!mem_pool_init(base,
step))

         {

                   fprintf(stderr,
"mem_pool_init error/n");

                   return
1;

         }

         print_mem_pool_info();

         alloc_test(ptr1);

         print_mem_pool_info();

        

         mem_free(ptr1[5]);

         print_mem_pool_info();

        

         alloc_test(ptr2);

         print_mem_pool_info();

        

         mem_pool_destroy();

        

}

#endif /* #ifdef MEM_POOL_DEBUG */

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息