一个简单的内存池的实现

来源:互联网 发布:手机内窥镜软件二维码 编辑:程序博客网 时间:2024/06/05 12:38

http://blog.csdn.net/bripengandre/article/details/3206018





当频繁地用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 */









5楼 yyd01245 2012-09-25 22:52发表 [回复]
写的真详细了,对我这种新手有帮助啊
4楼 airomyas 2011-10-27 21:24发表 [回复]
还有,那个销毁内存池的函数destory贴出来的是写的不对的
改成这样就好了:
void mem_pool_destory(void)
{
mem_block_t *prev, *cur;

prev = NULL;
cur = mem_pool.block_head;

while (cur != NULL)
{
prev = cur;
cur = cur->next;
free(prev->node_head);
free(prev); /* release prev itself */
}

memset(&mem_pool, 0, sizeof(mem_pool_t));
}
3楼 airomyas 2011-10-27 20:44发表 [回复]
这个内存池相当不错啊,这不是随便写写出来的。
2楼 LaoLiulaoliu 2010-12-22 22:01发表 [回复] [引用] [举报]
博主你好,看到你的《一个简单的内存池的实现》,感觉不错。我有一个问题,如何封装使得BUF_SIZE能被当做值传入。也就是使内存池的大小可变。我一直想改,但是我能力不够。知道C++ 可以用模板,但是C我不会。希望能够跟你探讨,谢谢。
Re: 彭令鹏 2011-01-09 19:13发表 [回复] [引用] [举报]
回复 LaoLiulaoliu:直接在初始化内存池函数里指定,内存池参考下apache的吧
Re: LaoLiulaoliu 2011-01-15 21:13发表 [回复] [引用] [举报]
回复 bripengandre:等我需要的时候再看apache吧,apache服务器的代码很多吧。
1楼 彭令鹏 2010-04-19 10:12发表 [回复]
这里节省的时间主要是系统调用的时间。


原创粉丝点击