当频繁地用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;
- 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;
- int free_cnt;
- 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。 代码:
-
-
-
-
- #ifndef _MEM_POOL_H_
- #define _MEM_POOL_H_
-
- #define BUF_SIZE 100
- #define BASE_COUNT 10000
- #define STEP_COUNT 1000
-
-
- 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;
- 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;
- int free_cnt;
- int base;
- int step;
- }mem_pool_t, *pmem_pool_t;
-
-
- int mem_pool_init(int base, int step);
- void mem_pool_destroy(void);
- void print_mem_pool_info(void);
-
- void *mem_alloc(void);
- void mem_free(void *ptr);
-
- #endif /* _MEM_POOL_H */
-
-
-
- #include "mem_pool.h"
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- #define MEM_POOL_DEBUG
-
-
- static int add_mem_block(int cnt);
-
- static int mem_block_init(int cnt, mem_block_t *block);
-
- static int free_list_init(const mem_block_t *block);
-
- static mem_pool_t mem_pool;
-
- int mem_pool_init(int base, int step)
- {
- if(base <= 0)
- {
- base = BASE_COUNT;
- }
- if(step <= 0)
- {
- step = STEP_COUNT;
- }
-
- memset(&mem_pool, 0, sizeof(mem_pool));
- mem_pool.base = base;
- mem_pool.step = step;
-
- 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");
- }
-
-
- void *mem_alloc(void)
- {
- mem_node_t *p;
-
- if(mem_pool.free_head == NULL)
- {
- if(!add_mem_block(mem_pool.step))
- {
- return NULL;
- }
- }
-
-
- p = mem_pool.free_head;
- mem_pool.free_head = p->next;
-
- mem_pool.free_cnt--;
- return p;
- }
-
- void mem_free(void *ptr)
- {
- if(ptr == NULL)
- {
- return;
- }
-
- ((mem_node_t *)ptr)->next = mem_pool.free_head;
- mem_pool.free_head = ptr;
-
- mem_pool.free_cnt++;
- }
-
-
- 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;
- }
-
- block->next = mem_pool.block_head;
- mem_pool.block_head = block;
- if(mem_pool.block_tail == NULL)
- {
- mem_pool.block_tail = block;
- }
-
-
- block->node_tail->next = mem_pool.free_head;
- mem_pool.free_head = block->node_head;
- mem_pool.free_cnt += cnt;
-
- mem_pool.block_cnt++;
- return 1;
- }
-
- 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;
- }
-
-
- static int free_list_init(const mem_block_t *block)
- {
- mem_node_t *p, *end;
- if(block == NULL)
- {
- return 0;
- }
-
- end = block->node_tail;
- for(p = block->node_head; p < end; p++)
- {
- p->next = (p+1);
- }
- p->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;
- step = 0;
- 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();
-
- 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 */
|