Chinaunix首页 | 论坛 | 博客
  • 博客访问: 230525
  • 博文数量: 55
  • 博客积分: 2010
  • 博客等级: 大尉
  • 技术积分: 530
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-22 17:59
文章分类

全部博文(55)

文章存档

2015年(2)

2011年(1)

2010年(1)

2009年(18)

2008年(16)

2007年(17)

我的朋友

分类: 服务器与存储

2015-07-31 10:19:37

    大多数libaprAPI都依赖于内存池,借助内存池,简化了内存块的管理。想像一下没有内存池系统的情况:你申请一些内存块就必需逐个释放它们,例如如果你申请了10个内存块,你必需释放10次,否则,你将遭受内存泄露的错误。内存池解决了这个令人感到繁琐的问题,在申请一个内存池之后,你可以从内存池中申请多个内存块,释放它们的时候,你所需要做的就是销毁内存池,这样你就可以释放所有的内存块了。这有两个优点,第一,它可以预防内存泄露的错误;第二,分配内存块的开销相对变低了。从某种意义上说,内存池迫使你遵循面向会话编程,一个内存池就是一种会话内容,这样,处于同一个内存池中的对象就有相同的生命周期,你可以通过控制会话内容来控制对象。在一个会话的开始,你创建了一个内存池,接着,你在内存池中创建了一些对象,你不需要去关心这些对象的生命周期,最后,在会话结束的时候,你只需要将那个内存池销毁就可以了。
    下面列举相关的API
1、apr_initialize()  初始化APR库
2、APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent);    创建一个内存池,这个内存池将一直存活,直到你调用apr_pool_destroy()函数以后被销毁,参数newpool是一个输出参数,是一个新创建的内存池对象。
3、APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);    获得一个指定内存大小的内存块。
4、APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);    销毁内存池
具体使用方法见mp-sample.c

/* 
摘自 mp-sample.c */
    apr_pool_t *mp;
    /* 
创建内存池 */
    apr_pool_create(&mp, NULL);

    /* 
从内存池中分配内存块 */
    char *buf1;
    buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);

简单地说,我们可以像使用malloc(3)这样使用apr_palloc(),也可以调用apr_pcalloc(),正如你猜到的,apr_pcalloc类似于calloc(3)apr_pcalloc返回一个被0填充了的内存块。假如你使用了malloc(3)/calloc(3),你需要调用free(3)来释放分配了的内存。但是在内存池中,你必不需要释放每个内存块,你只需要对该内存池调用apr_poll_destroy()函数从而释放所有的内存块。

注:使用apr_palloc()申请内存,其内存块的大小没有限制,然而,在内存池中申请大内存并不是什么好主意。内存池本质上是为了更小的内存块而设计的,实际上,初始的内存池的大小是8000字节。如果你需要申请超过几兆字节的内存块时,那么就不要使用内存池。
注:默认情况下,内存池管理器从不将申请到的内存归还给系统。如果程序要运行很长时间,这将是一个问题,推荐像下面的代码那样指定一个上限:

/* 
设置上限,让内存池管理器释放内存,将内存返回给系统的示例代码 */

#define YOUR_POOL_MAX_FREE_SIZE 32      /* apr_pool max free list size */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
apr_allocator_t* pa = apr_pool_allocator_get(mp);
if (pa) {
    apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE);
}

这儿有两个API函数需要知道,一个是apr_pool_clear(),另一个是apr_pool_cleanup_register()apr_pool_clear()类似于apr_pool_destroy(),不同的是内存池将一直存在。示例代码如下:
/* 
使用apr_pool_clear()的例子 */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
    do_operation(..., mp);
    apr_pool_clear(mp);
}
apr_pool_destroy(mp);

do_operation()
里使用了内存池,分配了一些内存块。假如在do_operation()之外不需要这些内存块了,可以调用apr_pool_clear()函数,这样能缩小内存的使用大小。如果你熟悉系统的栈内存的话,你会觉得内存池与栈内存一样,调用apr_palloc只是如同移动SP(栈指针),调用apr_pool_clear()如同重置SP,两者都是轻量级的操作。

使用apr_pool_cleanup_register()函数,可以在内存池清空/销毁上设定一个钩子(回调)函数,在内存池清空或是销毁后调用这个函数,在这个回调函数中,你可以实现任何在内存池上的结束代码。

关于内存池的最后一个主题是子池,每个内存池都可以有一个父内存池,因此,内存池构造了树。apr_pool_create()的第二个参数表示父内存池,当这个参数为NULL时,新创建的内存池将变为一个根内存池,可以在这个根内存池上创建子内存池。在这个树中对一个内存池调用apr_pool_destroy()函数,则该内存池的子内存池也将被销毁;当对该内存池调用apr_pool_clear()函数,则这个内存池存在但是它的子内存池将被销毁,上面提及到的那些清除函数,在子内存池销毁时被调用。

注:当将NULL值做为清除回调函数时将会产生一个bug,你必须像下面的代码那样传入apr_pool_cleanup_null
/* 
关于内存池典型bug的伪代码 */
/* apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, NULL); 
这将产生一个bug */
/* 
修正: */
apr_pool_cleanup_register(mp, ANY_CONTEXT_OF_YOUR_CODE, ANY_CALLBACK_OF_YOUR_CODE, apr_pool_cleanup_null);
阅读(1200) | 评论(0) | 转发(0) |
0

上一篇:SIP学习笔记

下一篇:没有了

给主人留下些什么吧!~~