[code] 也发一个简单基于pool的内存管理

时间:2008-01-16 11:14:30  来源:  作者:

这也是在 libscws 编写中用到的,因为在造树(XTree)的时候,经常碰到断断续续的动态申请内存,这时如果一次次 malloc 和 free 在代码书写上很不方便,而且还容易造成遗漏甚至导致内存浪费。

我这个小功能相当于建造一个自己的句柄,管理着自己申请过(仍是malloc)的内存,释放时统一释放,此外一次性会 malloc 一个较长的块(8192),尔后的申请从已申请中取即是。

缺点就是申请完直到最后才一起释放,没有自己的释放管理机制。好处就是可以避免自己在多次 malloc ,甚至在不同函数中 malloc 而又要到相应的其它函数去 free 时造成的麻烦。


/**
 * @file pool.h
 * @author Hightman Mar
 * @editor set number ; syntax on ; set autoindent ; set tabstop=4 (vim)
 * $Id: pool.h,v 1.1.1.1 2007/06/05 04:19:45 hightman Exp $
 */

#ifndef _SCWS_POOL_20070525_H_
#define _SCWS_POOL_20070525_H_

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

/* block size for pool */
#define POOL_BLK_SIZ 4096

/* data structure for pool */
struct pheap
{
int size;
int used;
char block[0];
};

struct pclean
{
void *obj;
struct pclean *nxt;
};

typedef struct
{
int size; /* total allocated */
int dirty; /* total wasted */
struct pheap *heap;
struct pclean *clean;
} pool_st, *pool_t;

/* pool: api */
pool_t pool_new(); /* create a new memory pool with an initial heap size */
void pool_free(pool_t p); /* frees all the data on the pool & delete the pool itself */
void *pmalloc(pool_t p, int size); /* wrapper around malloc, takes from the pool */
void *pmalloc_x(pool_t p, int size, char c); /* wrapper around pmalloc which prefills buffer with c */
void *pmalloc_z(pool_t p, int size); /* wrapper around pmalloc, which prefills by zero */
char *pstrdup(pool_t p, const char *s); /* wrapper around strdup, gains the mem from the pool */
char *pstrndup(pool_t p, const char *s, int l);

#endif




/**
 * @file pool.c
 * @author Hightman Mar
 * @editor set number ; syntax on ; set autoindent ; set tabstop=4 (vim)
 * $Id: pool.c,v 1.1.1.1 2007/06/05 04:19:45 hightman Exp $
 */

#include "pool.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

/** pool memory management */
static void _pool_append_clean(pool_t p, void *obj)
{
struct pclean *c;

p->size += sizeof(struct pclean);
c = (struct pclean *) malloc(sizeof(struct pclean));
c->obj = obj;
c->nxt = p->clean;
p->clean = c;
}

static void _pool_heap_new(pool_t p)
{
if (p->heap != NULL)
p->dirty += (p->heap->size - p->heap->used);

p->heap = (struct pheap *) malloc(POOL_BLK_SIZ);
p->heap->size = POOL_BLK_SIZ - sizeof(struct pheap);
p->heap->used = 0;
p->size += POOL_BLK_SIZ;

_pool_append_clean(p, (void *) p->heap);
}


pool_t pool_new()
{
pool_t p;

p = (pool_t) malloc(sizeof(pool_st));
p->size = sizeof(pool_st);
p->dirty = 0;
p->heap = NULL;
p->clean = NULL;
_pool_heap_new(p);
return p;
}

void pool_free(pool_t p)
{
struct pclean *cur, *nxt;

cur = p->clean;
while (cur != NULL)
{
free(cur->obj);
nxt = cur->nxt;
free(cur);
cur = nxt;
}
free(p);
}

void *pmalloc(pool_t p, int size)
{
void *block;

/* big request */
if (size > (p->heap->size / 4))
{
block = malloc(size);
p->size += size;
_pool_append_clean(p, block);
return block;
}

/* memory align (>=4) */
if (size & 0x04)
{
while (p->heap->used & 0x03)
{
p->dirty++;
p->heap->used++;
}
}

/* not enough? */
if (size > (p->heap->size - p->heap->used))
_pool_heap_new(p);

block = (void *)((char *) p->heap->block + p->heap->used);
p->heap->used += size;
return block;
}

void *pmalloc_x(pool_t p, int size, char c)
{
void *result = pmalloc(p, size);
memset(result, c, size);
return result;
}  

void *pmalloc_z(pool_t p, int size)
{
return pmalloc_x(p, size, 0);
}

char *pstrdup(pool_t p, const char *src)
{
char *dst;
int len;

if (src == NULL) 
return NULL;

len = strlen(src) + 1;
dst = (char *) pmalloc(p, len);
memcpy(dst, src, len);
return dst;
}

char *pstrndup(pool_t p, const char *src, int len)
{
char *dst;

if (src == NULL) 
return NULL;

dst = (char *) pmalloc(p, len + 1);
memcpy(dst, src, len);
dst[len] = '';

return dst;
}






 hightman 回复于:2007-06-09 13:18:27

用法大概是这样,只要记录一个p指针,其它申请就可以直接调用了

pool_t p = pool_new();

pmalloc(p, SIZE); 
pmalloc_z(p,size)
pstrdup(p,s);
pstrndup(p,s, size) ...

pool_free(p);


 phoneix 回复于:2007-06-09 13:53:15

能不能说明一下,只有代码看的很累


 hightman 回复于:2007-06-09 13:57:41

引用:原帖由 phoneix 于 2007-6-9 13:53 发表
能不能说明一下,只有代码看的很累 



我的上篇回贴大概已经说到了的。比如你建一些动态的树结构,难免要对key和data进行内存分配,最后删除树的时候,再一个个对照着释放内存。这样就有些不方便,我这里就用这个 pool 结构来管理这些内存,这样只要在适当的地方也可以全局也可以某个结构中的一个元素申明为 pool_t 指针并创建它,然后再申请的内存让它来管理,最后一起释放。


 phoneix 回复于:2007-06-09 13:57:55

没有做并发的控制啊?会不会不安全?


 12013396 回复于:2007-06-12 09:57:36

代码完成了内存批发转零售的功能,但没有针对特定大小进行优化,只是减少了向glibc申请内存的次数。


 xhl 回复于:2007-06-12 10:05:49

引用:原帖由 12013396 于 2007-6-12 09:57 发表
代码完成了内存批发转零售的功能,但没有针对特定大小进行优化,只是减少了向glibc申请内存的次数。 




真形象    批发零售。。。。:D


 mq110 回复于:2007-06-12 10:42:32

引用:原帖由 phoneix 于 2007-6-9 13:57 发表
没有做并发的控制啊?会不会不安全? 



是啊,感觉少了PV的东东.


 mmgg00 回复于:2007-07-09 11:22:46

jabber就是这么用的


 hightman 回复于:2007-07-09 11:26:06

引用:原帖由 mmgg00 于 2007-7-9 11:22 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7025120&ptid=947062]
jabber就是这么用的 




没错,这段代码起初就是看jabberd2.0s10时读到的,但感觉他的处理太复杂,我就简化自行编写了一下,所以有些函数名也没变呢....


 cugb_cat 回复于:2007-07-09 12:05:55

没锁,多线程环境不敢用啊


 福瑞哈哥 回复于:2007-07-09 13:38:44

赞同6楼的观点:没有对特定大小进行优化。
我觉得内存池很多情况下都是对一种特定数据类型的存储管理,比如一个整数、一个树结构等。对不同的数据结构最好使用不同的内存池或者子内存池。特定长度的内存池设计简单,使用起来效率也很高,对齐就以数组方式使用就可以了。


 xhl 回复于:2007-07-09 14:55:28

不是特殊需要的话, 尽量不要自己进行内存管理。。。


因为OS也会做内存管理, 用户也自己做的话, 容易在逻辑上打架, 产生效率下降。。。


 福瑞哈哥 回复于:2007-07-09 14:57:39

引用:原帖由 xhl 于 2007-7-9 14:55 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=7026297&ptid=947062]
不是特殊需要的话, 尽量不要自己进行内存管理。。。


因为OS也会做内存管理, 用户也自己做的话, 容易在逻辑上打架, 产生效率下降。。。 



基本上是这样的。不过在设计一个容器比如说树时,可以加一个空闲结点队列以复用。这也是一种内存池。




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=947062
转载请注明作者名及原文出处


文章评论

共有 位网友发表了评论 查看完整内容